Java tutorial
/* * Copyright 2017 The Chromium Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ package io.flutter.actions; import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.process.OSProcessHandler; import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.vfs.VirtualFile; import io.flutter.FlutterBundle; import io.flutter.FlutterMessages; import io.flutter.pub.PubRoot; import io.flutter.pub.PubRoots; import io.flutter.sdk.FlutterSdk; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; public class OpenInAndroidStudioAction extends AnAction { private static final String LABEL_FILE = FlutterBundle.message("flutter.androidstudio.open.file.text"); private static final String DESCR_FILE = FlutterBundle.message("flutter.androidstudio.open.file.description"); private static final String LABEL_MODULE = FlutterBundle.message("flutter.androidstudio.open.module.text"); private static final String DESCR_MODULE = FlutterBundle .message("flutter.androidstudio.open.module.description"); @Override public void update(AnActionEvent event) { updatePresentation(event, event.getPresentation()); } @Override public void actionPerformed(AnActionEvent e) { final String androidStudioPath = findAndroidStudio(e.getProject()); if (androidStudioPath == null) { FlutterMessages.showError("Error Opening Android Studio", "Unable to locate Android Studio."); return; } final VirtualFile projectFile = findProjectFile(e); if (projectFile == null) { FlutterMessages.showError("Error Opening Android Studio", "Project not found."); return; } final VirtualFile file = e.getData(CommonDataKeys.VIRTUAL_FILE); final String sourceFile = file == null ? null : file.isDirectory() ? null : file.getPath(); openFileInStudio(projectFile, androidStudioPath, sourceFile); } private static void updatePresentation(AnActionEvent event, Presentation state) { if (findProjectFile(event) == null) { state.setVisible(false); } else { VirtualFile file = event.getData(CommonDataKeys.VIRTUAL_FILE); String label, descr; if (file != null && !file.isDirectory()) { // The file will be opened in an editor in the new IDE window. label = LABEL_FILE; descr = DESCR_FILE; } else { // The new IDE window will be opened on the Android module but there is no file selected for editing. label = LABEL_MODULE; descr = DESCR_MODULE; } state.setVisible(true); state.setText(label); state.setDescription(descr); } } protected static boolean isProjectFileName(String name) { // Note: If the project content is rearranged to have the android module file within the android directory, this will fail. return name.endsWith("_android.iml"); } // A plugin contains an example app, which needs to be opened when the native Android is to be edited. // In the case of an app that contains a plugin the flutter_app/flutter_plugin/example/android should be opened when // 'Open in Android Studio' is requested. protected static VirtualFile findProjectFile(@Nullable AnActionEvent e) { if (e != null) { final VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(e.getDataContext()); if (file != null && file.exists()) { // We have a selection. Check if it is within a plugin. final Project project = e.getProject(); assert (project != null); // Return null if this is an ios folder. if (FlutterExternalIdeActionGroup.isWithinIOsDirectory(file, project)) { return null; } final VirtualFile projectDir = project.getBaseDir(); for (PubRoot root : PubRoots.forProject(project)) { if (root.isFlutterPlugin()) { VirtualFile rootFile = root.getRoot(); VirtualFile aFile = file; while (aFile != null) { if (aFile.equals(rootFile)) { // We know a plugin resource is selected. Find the example app for it. for (VirtualFile child : rootFile.getChildren()) { if (isExampleWithAndroidWithApp(child)) { return child.findChild("android"); } } } if (aFile.equals(projectDir)) { aFile = null; } else { aFile = aFile.getParent(); } } } } if (isProjectFileName(file.getName())) { return getProjectForFile(file); } } final Project project = e.getProject(); if (project != null) { return getProjectForFile(findStudioProjectFile(project)); } } return null; } private static void openFileInStudio(@NotNull VirtualFile projectFile, @NotNull String androidStudioPath, @Nullable String sourceFile) { try { final GeneralCommandLine cmd; if (SystemInfo.isMac) { cmd = new GeneralCommandLine().withExePath("open").withParameters("-a", androidStudioPath, "--args", projectFile.getPath()); if (sourceFile != null) { cmd.addParameter(sourceFile); } } else { // TODO Open editor on sourceFile for Linux, Windows cmd = new GeneralCommandLine().withExePath(androidStudioPath).withParameters(projectFile.getPath()); } final OSProcessHandler handler = new OSProcessHandler(cmd); handler.addProcessListener(new ProcessAdapter() { @Override public void processTerminated(@NotNull final ProcessEvent event) { if (event.getExitCode() != 0) { FlutterMessages.showError("Error Opening", projectFile.getPath()); } } }); handler.startNotify(); } catch (ExecutionException ex) { FlutterMessages.showError("Error Opening", "Exception: " + ex.getMessage()); } } @Nullable private static VirtualFile findStudioProjectFile(@NotNull Project project) { for (PubRoot root : PubRoots.forProject(project)) { for (VirtualFile child : root.getRoot().getChildren()) { if (isProjectFileName(child.getName())) { return child; } } } return null; } @Nullable private static String findAndroidStudio(@Nullable Project project) { if (project == null) { return null; } final FlutterSdk flutterSdk = FlutterSdk.getFlutterSdk(project); if (flutterSdk != null) { String androidSdkLocation = flutterSdk.queryFlutterConfig("android-studio-dir", true); if (androidSdkLocation != null) { if (androidSdkLocation.contains("/Android Studio 2")) { Messages.showErrorDialog(FlutterBundle.message("old.android.studio.message", File.separator), FlutterBundle.message("old.android.studio.title")); return null; } if (androidSdkLocation.endsWith("/")) { androidSdkLocation = androidSdkLocation.substring(0, androidSdkLocation.length() - 1); } final String contents = "/Contents"; // On a mac, trim off "/Contents". if (SystemInfo.isMac && androidSdkLocation.endsWith(contents)) { return androidSdkLocation.substring(0, androidSdkLocation.length() - contents.length()); } return androidSdkLocation; } } return null; } @Nullable private static VirtualFile getProjectForFile(@Nullable VirtualFile file) { // Expect true: isProjectFileName(file.getName()), but some flexibility is allowed. if (file == null) { return null; } if (file.isDirectory()) { return isAndroidWithApp(file) ? file : null; } VirtualFile dir = file.getParent(); if (isAndroidWithApp(dir)) { // In case someone moves the .iml file, or the project organization gets rationalized. return dir; } VirtualFile project = dir.findChild("android"); if (project != null && isAndroidWithApp(project)) { return project; } return null; } // Return true if the directory is named android and contains either an app (for applications) or a src (for plugins) directory. private static boolean isAndroidWithApp(@NotNull VirtualFile file) { return file.getName().equals("android") && (file.findChild("app") != null || file.findChild("src") != null); } // Return true if the directory has the structure of a plugin example application: a pubspec.yaml and an // android directory with an app. The example app directory name is not specified in case it gets renamed. private static boolean isExampleWithAndroidWithApp(@NotNull VirtualFile file) { boolean hasPubspec = false; boolean hasAndroid = false; for (VirtualFile candidate : file.getChildren()) { if (isAndroidWithApp(candidate)) hasAndroid = true; if (candidate.getName().equals("pubspec.yaml")) hasPubspec = true; if (hasAndroid && hasPubspec) { return true; } } return false; } }