| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.google.appindexing.ui; |
| |
| import com.android.tools.analytics.UsageTracker; |
| import com.android.tools.idea.stats.UsageTrackerUtils; |
| import com.google.appindexing.api.ApiCreator; |
| import com.google.appindexing.util.AppIndexingBundle; |
| import com.google.urlassistant.ActivityData; |
| import com.google.urlassistant.util.ManifestUtils; |
| import com.google.wireless.android.sdk.stats.AndroidStudioEvent; |
| import com.intellij.ide.BrowserUtil; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.ModalityState; |
| import com.intellij.openapi.application.Result; |
| import com.intellij.openapi.command.WriteCommandAction; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.fileEditor.FileEditorManager; |
| import com.intellij.openapi.module.ModuleManager; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.DialogWrapper; |
| import com.intellij.openapi.ui.Messages; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.PsiDocumentManager; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.ui.DoubleClickListener; |
| import com.intellij.ui.components.JBLabel; |
| import com.intellij.ui.components.JBList; |
| import com.intellij.ui.components.JBPanel; |
| import com.intellij.ui.components.JBScrollPane; |
| import com.intellij.uiDesigner.core.GridConstraints; |
| import com.intellij.uiDesigner.core.GridLayoutManager; |
| import icons.StudioIcons; |
| import java.awt.BorderLayout; |
| import java.awt.Component; |
| import java.awt.Dimension; |
| import java.awt.event.MouseEvent; |
| import java.util.List; |
| import javax.swing.AbstractListModel; |
| import javax.swing.Action; |
| import javax.swing.BorderFactory; |
| import javax.swing.JComponent; |
| import javax.swing.JList; |
| import javax.swing.JPanel; |
| import javax.swing.JTextPane; |
| import javax.swing.ListSelectionModel; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| /** |
| * A dialog to select activity for code inserting. |
| */ |
| public class InsertApiCodeDialog extends DialogWrapper { |
| |
| private final Project myProject; |
| private final FileEditorManager myFileEditorManager; |
| |
| |
| private JList myJList; |
| private JPanel myContentPanel = new JBPanel(new BorderLayout(0, 0)); |
| private final JTextPane myWarningMessage = new JTextPane(); |
| private final JBPanel myMessagePanel = new JBPanel(new GridLayoutManager(1, 2)); |
| |
| // This list contains all activities which support app links. |
| private List<ActivityData> myActivities; |
| private ApiCreator myCreator; |
| |
| @Nullable |
| @Override |
| protected JComponent createCenterPanel() { |
| return myContentPanel; |
| } |
| |
| @Override |
| public JComponent getPreferredFocusedComponent() { |
| return myJList; |
| } |
| |
| public InsertApiCodeDialog(@NotNull Project project) { |
| super(project); |
| myProject = project; |
| myFileEditorManager = FileEditorManager.getInstance(myProject); |
| |
| myMessagePanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0)); |
| GridConstraints constraints = new GridConstraints(); |
| constraints.setAnchor(GridConstraints.ANCHOR_WEST); |
| JBLabel iconLabel = new JBLabel(); |
| iconLabel.setIcon(StudioIcons.Common.ERROR); |
| myMessagePanel.add(iconLabel, constraints); |
| |
| constraints.setColumn(1); |
| myWarningMessage.setAlignmentX(Component.LEFT_ALIGNMENT); |
| myWarningMessage.setBackground(myContentPanel.getBackground()); |
| myMessagePanel.add(myWarningMessage, constraints); |
| myMessagePanel.setVisible(false); |
| |
| myContentPanel.setPreferredSize(new Dimension(400, 200)); |
| myContentPanel.add(myMessagePanel, BorderLayout.SOUTH); |
| |
| |
| initializeActivityList(); |
| JBScrollPane scrollPane = new JBScrollPane(myJList); |
| myContentPanel.add(scrollPane, BorderLayout.CENTER); |
| |
| getOKAction().putValue(Action.NAME, AppIndexingBundle.message("app.indexing.code.insert.dialog.ok.button")); |
| setOKActionEnabled(false); |
| setTitle(AppIndexingBundle.message("app.indexing.code.insert.dialog.title")); |
| init(); |
| } |
| |
| private void initializeActivityList() { |
| ApplicationManager.getApplication().invokeAndWait( |
| () -> myActivities = ManifestUtils.getAllActivities(ModuleManager.getInstance(myProject).getModules(), true, true), |
| ModalityState.any()); |
| AbstractListModel model = new AbstractListModel() { |
| @Override |
| public int getSize() { |
| return myActivities.size(); |
| } |
| |
| @Override |
| public Object getElementAt(int i) { |
| return myActivities.get(i).getActivityAndModuleName(); |
| } |
| }; |
| |
| myJList = new JBList(model); |
| if (myActivities.isEmpty()) { |
| myWarningMessage.setText(AppIndexingBundle.message("app.indexing.code.insert.error.deep_link_missing")); |
| myMessagePanel.setVisible(true); |
| return; |
| } |
| myJList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); |
| |
| |
| myJList.addListSelectionListener(e -> { |
| if (!e.getValueIsAdjusting() && myJList.getSelectedValue() != null) { |
| handleListMouseClickEvent(myJList.getSelectedIndex()); |
| } |
| }); |
| |
| new DoubleClickListener() { |
| @Override |
| protected boolean onDoubleClick(MouseEvent e) { |
| if (isOKActionEnabled()) { |
| doOKAction(); |
| return true; |
| } |
| return false; |
| } |
| }.installOn(myJList); |
| } |
| |
| private void handleListMouseClickEvent(int index) { |
| setOKActionEnabled(false); |
| // Opens the selected activity source file in file editor. |
| ActivityData activityData = myActivities.get(index); |
| VirtualFile file = activityData.getActivityVirtualFile(); |
| // file must be valid here because all activities without virtual file have already been removed previously. |
| assert file != null; |
| ApplicationManager.getApplication().invokeAndWait(() -> { |
| myFileEditorManager.openFile(file, true); |
| }, ModalityState.any()); |
| Editor editor = myFileEditorManager.getSelectedTextEditor(); |
| assert editor != null; |
| |
| PsiFile selectedPsiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(editor.getDocument()); |
| assert selectedPsiFile != null; |
| |
| myCreator = new ApiCreator(myProject, selectedPsiFile, activityData.getActivityClass()); |
| ApiCreator.InsertStatusCode statusCode = myCreator.eligibleForInsertingAppIndexingApiCode(); |
| if (statusCode != ApiCreator.InsertStatusCode.SUCCESS) { |
| myWarningMessage.setText(statusCode.getMessage()); |
| myMessagePanel.setVisible(true); |
| return; |
| } |
| myMessagePanel.setVisible(false); |
| setOKActionEnabled(true); |
| } |
| |
| @Override |
| protected void doOKAction() { |
| try { |
| new WriteCommandAction(myProject) { |
| @Override |
| protected void run(@NotNull final Result result) throws Throwable { |
| ApiCreator.InsertStatusCode statusCode = myCreator.insertAppIndexingApiCodeForActivity(); |
| if (statusCode != ApiCreator.InsertStatusCode.SUCCESS) { |
| Messages.showErrorDialog(statusCode.getMessage(), AppIndexingBundle.message("app.indexing.code.insert.error.title")); |
| return; |
| } |
| UsageTracker.log(UsageTrackerUtils.withProjectId( |
| AndroidStudioEvent.newBuilder() |
| .setCategory(AndroidStudioEvent.EventCategory.APP_INDEXING) |
| .setKind(AndroidStudioEvent.EventKind.APP_INDEXING_API_CODE_CREATED), |
| myProject)); |
| } |
| }.execute(); |
| } |
| finally { |
| super.doOKAction(); |
| } |
| } |
| |
| @Nullable |
| @Override |
| protected String getHelpId() { |
| return "https://firebase.google.com/docs/app-indexing/android/migrate"; |
| } |
| |
| @Override |
| protected void doHelpAction() { |
| String helpId = getHelpId(); |
| assert helpId != null; // Otherwise, doHelpAction would not be triggered |
| BrowserUtil.browse(helpId); |
| } |
| } |