blob: 65697d1043d037ff6ab2653c4930070f53f618a6 [file] [log] [blame]
/*
* Copyright 2016 The Bazel Authors. All rights reserved.
*
* 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.idea.blaze.android.run.binary;
import com.android.tools.idea.run.activity.ActivityLocatorUtils;
import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryLaunchMethodsProvider.AndroidBinaryLaunchMethod;
import com.google.idea.blaze.android.run.binary.BlazeAndroidBinaryLaunchMethodsProvider.AndroidBinaryLaunchMethodComboEntry;
import com.google.idea.blaze.base.run.state.RunConfigurationState;
import com.google.idea.blaze.base.run.state.RunConfigurationStateEditor;
import com.google.idea.blaze.base.ui.IntegerTextField;
import com.google.idea.blaze.base.ui.UiUtil;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.ide.util.TreeClassChooser;
import com.intellij.ide.util.TreeClassChooserFactory;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.fileTypes.PlainTextLanguage;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComponentWithBrowseButton;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Key;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.ProjectScope;
import com.intellij.ui.EditorTextField;
import com.intellij.ui.LanguageTextField;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import java.awt.Color;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ResourceBundle;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.border.TitledBorder;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.android.util.AndroidUtils;
/**
* The part of the Blaze Android Binary handler editor that allows the user to pick an activity to
* launch. Patterned after {@link org.jetbrains.android.run.ApplicationRunParameters}.
*/
class BlazeAndroidBinaryRunConfigurationStateEditor implements RunConfigurationStateEditor {
public static final Key<BlazeAndroidBinaryRunConfigurationStateEditor>
ACTIVITY_CLASS_TEXT_FIELD_KEY = Key.create("BlazeActivityClassTextField");
private final RunConfigurationStateEditor commonStateEditor;
private JPanel panel;
private ComponentWithBrowseButton<EditorTextField> activityField;
private JRadioButton launchNothingButton;
private JRadioButton launchDefaultButton;
private JRadioButton launchCustomButton;
private JComboBox<AndroidBinaryLaunchMethodComboEntry> launchMethodComboBox;
private JCheckBox splitApksCheckBox;
private JCheckBox useWorkProfileIfPresentCheckBox;
private JLabel userIdLabel;
private IntegerTextField userIdField;
private boolean componentEnabled = true;
BlazeAndroidBinaryRunConfigurationStateEditor(
RunConfigurationStateEditor commonStateEditor, Project project) {
this.commonStateEditor = commonStateEditor;
setupUI(project);
userIdField.setMinValue(0);
activityField.addActionListener(
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (!project.isInitialized()) {
return;
}
// We find all Activity classes in the module for the selected variant
// (or any of its deps).
final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
PsiClass activityBaseClass =
facade.findClass(
AndroidUtils.ACTIVITY_BASE_CLASS_NAME, ProjectScope.getAllScope(project));
if (activityBaseClass == null) {
Messages.showErrorDialog(
panel, AndroidBundle.message("cant.find.activity.class.error"));
return;
}
GlobalSearchScope searchScope = GlobalSearchScope.projectScope(project);
PsiClass initialSelection =
facade.findClass(activityField.getChildComponent().getText(), searchScope);
TreeClassChooser chooser =
TreeClassChooserFactory.getInstance(project)
.createInheritanceClassChooser(
"Select Activity Class",
searchScope,
activityBaseClass,
initialSelection,
null);
chooser.showDialog();
PsiClass selClass = chooser.getSelected();
if (selClass != null) {
// This must be done because Android represents
// inner static class paths differently than java.
String qualifiedActivityName =
ActivityLocatorUtils.getQualifiedActivityName(selClass);
activityField.getChildComponent().setText(qualifiedActivityName);
}
}
});
ActionListener listener = e -> updateEnabledState();
launchCustomButton.addActionListener(listener);
launchDefaultButton.addActionListener(listener);
launchNothingButton.addActionListener(listener);
launchMethodComboBox.addActionListener(
e ->
splitApksCheckBox.setVisible(
((AndroidBinaryLaunchMethodComboEntry) launchMethodComboBox.getSelectedItem())
.launchMethod.equals(AndroidBinaryLaunchMethod.MOBILE_INSTALL) // v1 only
));
useWorkProfileIfPresentCheckBox.addActionListener(e -> updateEnabledState());
}
@Override
public void resetEditorFrom(RunConfigurationState genericState) {
BlazeAndroidBinaryRunConfigurationState state =
(BlazeAndroidBinaryRunConfigurationState) genericState;
commonStateEditor.resetEditorFrom(state.getCommonState());
boolean launchSpecificActivity =
state.getMode().equals(BlazeAndroidBinaryRunConfigurationState.LAUNCH_SPECIFIC_ACTIVITY);
if (state.getMode().equals(BlazeAndroidBinaryRunConfigurationState.LAUNCH_DEFAULT_ACTIVITY)) {
launchDefaultButton.setSelected(true);
} else if (launchSpecificActivity) {
launchCustomButton.setSelected(true);
} else {
launchNothingButton.setSelected(true);
}
if (launchSpecificActivity) {
activityField.getChildComponent().setText(state.getActivityClass());
}
for (int i = 0; i < launchMethodComboBox.getItemCount(); ++i) {
if (launchMethodComboBox.getItemAt(i).launchMethod.equals(state.getLaunchMethod())) {
launchMethodComboBox.setSelectedIndex(i);
break;
}
}
splitApksCheckBox.setSelected(state.useSplitApksIfPossible());
useWorkProfileIfPresentCheckBox.setSelected(state.useWorkProfileIfPresent());
userIdField.setValue(state.getUserId());
splitApksCheckBox.setVisible(
state.getLaunchMethod().equals(AndroidBinaryLaunchMethod.MOBILE_INSTALL));
updateEnabledState();
}
@Override
public void applyEditorTo(RunConfigurationState genericState) {
BlazeAndroidBinaryRunConfigurationState state =
(BlazeAndroidBinaryRunConfigurationState) genericState;
commonStateEditor.applyEditorTo(state.getCommonState());
state.setUserId((Integer) userIdField.getValue());
if (launchDefaultButton.isSelected()) {
state.setMode(BlazeAndroidBinaryRunConfigurationState.LAUNCH_DEFAULT_ACTIVITY);
} else if (launchCustomButton.isSelected()) {
state.setMode(BlazeAndroidBinaryRunConfigurationState.LAUNCH_SPECIFIC_ACTIVITY);
state.setActivityClass(activityField.getChildComponent().getText());
} else {
state.setMode(BlazeAndroidBinaryRunConfigurationState.DO_NOTHING);
}
state.setLaunchMethod(
((AndroidBinaryLaunchMethodComboEntry) launchMethodComboBox.getSelectedItem())
.launchMethod);
state.setUseSplitApksIfPossible(splitApksCheckBox.isSelected());
state.setUseWorkProfileIfPresent(useWorkProfileIfPresentCheckBox.isSelected());
}
@Override
public JComponent createComponent() {
return UiUtil.createBox(commonStateEditor.createComponent(), panel);
}
private void updateEnabledState() {
boolean useWorkProfile = useWorkProfileIfPresentCheckBox.isSelected();
userIdLabel.setEnabled(componentEnabled && !useWorkProfile);
userIdField.setEnabled(componentEnabled && !useWorkProfile);
commonStateEditor.setComponentEnabled(componentEnabled);
activityField.setEnabled(componentEnabled && launchCustomButton.isSelected());
launchNothingButton.setEnabled(componentEnabled);
launchDefaultButton.setEnabled(componentEnabled);
launchCustomButton.setEnabled(componentEnabled);
launchMethodComboBox.setEnabled(componentEnabled);
splitApksCheckBox.setEnabled(componentEnabled);
useWorkProfileIfPresentCheckBox.setEnabled(componentEnabled);
}
@Override
public void setComponentEnabled(boolean enabled) {
componentEnabled = enabled;
updateEnabledState();
}
private void createUIComponents(Project project) {
final EditorTextField editorTextField =
new LanguageTextField(PlainTextLanguage.INSTANCE, project, "") {
@Override
protected EditorEx createEditor() {
final EditorEx editor = super.createEditor();
final PsiFile file =
PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
if (file != null) {
DaemonCodeAnalyzer.getInstance(project).setHighlightingEnabled(file, false);
}
editor.putUserData(
ACTIVITY_CLASS_TEXT_FIELD_KEY, BlazeAndroidBinaryRunConfigurationStateEditor.this);
return editor;
}
};
activityField = new ComponentWithBrowseButton<EditorTextField>(editorTextField, null);
}
/** Initially generated by IntelliJ from a .form file, then checked in as source. */
private void setupUI(Project project) {
createUIComponents(project);
panel = new JPanel();
panel.setLayout(new GridLayoutManager(5, 2, new Insets(0, 0, 0, 0), -1, -1));
final JPanel activityPanel = new JPanel();
activityPanel.setLayout(new GridLayoutManager(4, 2, new Insets(0, 0, 0, 0), -1, -1));
panel.add(
activityPanel,
new GridConstraints(
3,
0,
1,
2,
GridConstraints.ANCHOR_CENTER,
GridConstraints.FILL_BOTH,
GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
null,
null,
null,
0,
false));
activityPanel.setBorder(
BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(),
"Activity",
TitledBorder.DEFAULT_JUSTIFICATION,
TitledBorder.DEFAULT_POSITION,
new Font(
activityPanel.getFont().getName(),
activityPanel.getFont().getStyle(),
activityPanel.getFont().getSize()),
new Color(-16777216)));
final JPanel userPanel = new JPanel();
userPanel.setLayout(new GridLayoutManager(2, 2, new Insets(0, 0, 0, 0), -1, -1));
panel.add(
userPanel,
new GridConstraints(
4,
0,
1,
2,
GridConstraints.ANCHOR_CENTER,
GridConstraints.FILL_BOTH,
GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
null,
null,
null,
0,
false));
userPanel.setBorder(
BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(),
"User",
TitledBorder.DEFAULT_JUSTIFICATION,
TitledBorder.DEFAULT_POSITION,
new Font(
userPanel.getFont().getName(),
userPanel.getFont().getStyle(),
userPanel.getFont().getSize()),
new Color(-16777216)));
launchNothingButton = new JRadioButton();
this.loadButtonText(
launchNothingButton,
ResourceBundle.getBundle("messages/AndroidBundle")
.getString("android.run.configuration.do.nothing.label"));
activityPanel.add(
launchNothingButton,
new GridConstraints(
0,
0,
1,
2,
GridConstraints.ANCHOR_CENTER,
GridConstraints.FILL_HORIZONTAL,
GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
GridConstraints.SIZEPOLICY_FIXED,
null,
null,
null,
0,
false));
launchDefaultButton = new JRadioButton();
launchDefaultButton.setText("Launch default Activity");
launchDefaultButton.setMnemonic('L');
launchDefaultButton.setDisplayedMnemonicIndex(0);
activityPanel.add(
launchDefaultButton,
new GridConstraints(
1,
0,
1,
2,
GridConstraints.ANCHOR_CENTER,
GridConstraints.FILL_HORIZONTAL,
GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
GridConstraints.SIZEPOLICY_FIXED,
null,
null,
null,
0,
false));
launchCustomButton = new JRadioButton();
launchCustomButton.setText("Launch:");
launchCustomButton.setMnemonic('A');
launchCustomButton.setDisplayedMnemonicIndex(1);
activityPanel.add(
launchCustomButton,
new GridConstraints(
2,
0,
1,
1,
GridConstraints.ANCHOR_WEST,
GridConstraints.FILL_NONE,
GridConstraints.SIZEPOLICY_FIXED,
GridConstraints.SIZEPOLICY_FIXED,
null,
null,
null,
0,
false));
activityPanel.add(
activityField,
new GridConstraints(
2,
1,
1,
1,
GridConstraints.ANCHOR_CENTER,
GridConstraints.FILL_BOTH,
GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
GridConstraints.SIZEPOLICY_FIXED,
null,
null,
null,
0,
false));
useWorkProfileIfPresentCheckBox = new JCheckBox();
useWorkProfileIfPresentCheckBox.setText(" Use work profile if present");
userPanel.add(
useWorkProfileIfPresentCheckBox,
new GridConstraints(
0,
0,
1,
2,
GridConstraints.ANCHOR_WEST,
GridConstraints.FILL_NONE,
GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
GridConstraints.SIZEPOLICY_FIXED,
null,
null,
null,
0,
false));
userIdLabel = new JLabel();
userIdLabel.setText("User ID");
userPanel.add(
userIdLabel,
new GridConstraints(
1,
0,
1,
1,
GridConstraints.ANCHOR_WEST,
GridConstraints.FILL_NONE,
GridConstraints.SIZEPOLICY_FIXED,
GridConstraints.SIZEPOLICY_FIXED,
null,
null,
null,
1,
false));
userIdField = new IntegerTextField();
userPanel.add(
userIdField,
new GridConstraints(
1,
1,
1,
1,
GridConstraints.ANCHOR_WEST,
GridConstraints.FILL_HORIZONTAL,
GridConstraints.SIZEPOLICY_WANT_GROW,
GridConstraints.SIZEPOLICY_FIXED,
null,
null,
null,
0,
false));
launchMethodComboBox =
new JComboBox<>(BlazeAndroidBinaryLaunchMethodsProvider.getAllLaunchMethods(project));
panel.add(
launchMethodComboBox,
new GridConstraints(
0,
0,
1,
2,
GridConstraints.ANCHOR_WEST,
GridConstraints.FILL_NONE,
GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
GridConstraints.SIZEPOLICY_FIXED,
null,
null,
null,
0,
false));
splitApksCheckBox = new JCheckBox();
splitApksCheckBox.setText("Use --split_apks where possible");
panel.add(
splitApksCheckBox,
new GridConstraints(
1,
0,
1,
2,
GridConstraints.ANCHOR_WEST,
GridConstraints.FILL_NONE,
GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
GridConstraints.SIZEPOLICY_FIXED,
null,
null,
null,
0,
false));
ButtonGroup buttonGroup;
buttonGroup = new ButtonGroup();
buttonGroup.add(launchDefaultButton);
buttonGroup.add(launchCustomButton);
buttonGroup.add(launchNothingButton);
}
/** Initially generated by IntelliJ from a .form file. */
private void loadButtonText(AbstractButton component, String text) {
StringBuffer result = new StringBuffer();
boolean haveMnemonic = false;
char mnemonic = '\0';
int mnemonicIndex = -1;
for (int i = 0; i < text.length(); i++) {
if (text.charAt(i) == '&') {
i++;
if (i == text.length()) {
break;
}
if (!haveMnemonic && text.charAt(i) != '&') {
haveMnemonic = true;
mnemonic = text.charAt(i);
mnemonicIndex = result.length();
}
}
result.append(text.charAt(i));
}
component.setText(result.toString());
if (haveMnemonic) {
component.setMnemonic(mnemonic);
component.setDisplayedMnemonicIndex(mnemonicIndex);
}
}
}