blob: ddcbf9a665f9c1b76463227a5599d9092982d734 [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.BlazeAndroidRunConfigurationState;
import com.google.idea.blaze.android.run.BlazeAndroidRunConfigurationStateEditor;
import com.google.idea.blaze.android.run.binary.instantrun.InstantRunExperiment;
import com.google.idea.blaze.base.ui.IntegerTextField;
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.Component;
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.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;
import org.jetbrains.annotations.Nullable;
/**
* 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 BlazeAndroidRunConfigurationStateEditor {
public static final Key<BlazeAndroidBinaryRunConfigurationStateEditor>
ACTIVITY_CLASS_TEXT_FIELD_KEY = Key.create("BlazeActivityClassTextField");
private final Project project;
@Nullable private JPanel panel;
private ComponentWithBrowseButton<EditorTextField> activityField;
private JRadioButton launchNothingButton;
private JRadioButton launchDefaultButton;
private JRadioButton launchCustomButton;
private JCheckBox mobileInstallCheckBox;
private JCheckBox splitApksCheckBox;
private JCheckBox instantRunCheckBox;
private JCheckBox useWorkProfileIfPresentCheckBox;
private JLabel userIdLabel;
private IntegerTextField userIdField;
BlazeAndroidBinaryRunConfigurationStateEditor(Project project) {
this.project = project;
setupUI();
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 -> activityField.setEnabled(launchCustomButton.isSelected());
launchCustomButton.addActionListener(listener);
launchDefaultButton.addActionListener(listener);
launchNothingButton.addActionListener(listener);
instantRunCheckBox.setVisible(InstantRunExperiment.INSTANT_RUN_ENABLED.getValue());
/** Only one of mobile-install and instant run can be selected at any one time */
mobileInstallCheckBox.addActionListener(
e -> {
if (mobileInstallCheckBox.isSelected()) {
instantRunCheckBox.setSelected(false);
}
});
instantRunCheckBox.addActionListener(
e -> {
if (instantRunCheckBox.isSelected()) {
mobileInstallCheckBox.setSelected(false);
}
});
mobileInstallCheckBox.addActionListener(
e -> splitApksCheckBox.setVisible(mobileInstallCheckBox.isSelected()));
useWorkProfileIfPresentCheckBox.addActionListener(
e -> {
setUserIdEnabled(!useWorkProfileIfPresentCheckBox.isSelected());
});
}
@Override
public void resetEditorFrom(BlazeAndroidRunConfigurationState state) {
BlazeAndroidBinaryRunConfigurationState configState =
(BlazeAndroidBinaryRunConfigurationState) state;
boolean launchSpecificActivity =
configState
.getMode()
.equals(BlazeAndroidBinaryRunConfigurationState.LAUNCH_SPECIFIC_ACTIVITY);
if (configState
.getMode()
.equals(BlazeAndroidBinaryRunConfigurationState.LAUNCH_DEFAULT_ACTIVITY)) {
launchDefaultButton.setSelected(true);
} else if (launchSpecificActivity) {
launchCustomButton.setSelected(true);
} else {
launchNothingButton.setSelected(true);
}
activityField.setEnabled(launchSpecificActivity);
if (launchSpecificActivity) {
activityField.getChildComponent().setText(configState.getActivityClass());
}
mobileInstallCheckBox.setSelected(configState.mobileInstall());
splitApksCheckBox.setSelected(configState.useSplitApksIfPossible());
instantRunCheckBox.setSelected(configState.instantRun());
useWorkProfileIfPresentCheckBox.setSelected(configState.useWorkProfileIfPresent());
userIdField.setValue(configState.getUserId());
setUserIdEnabled(!configState.useWorkProfileIfPresent());
splitApksCheckBox.setVisible(configState.mobileInstall());
}
private void setUserIdEnabled(boolean enabled) {
userIdLabel.setEnabled(enabled);
userIdField.setEnabled(enabled);
}
@Override
public Component getComponent() {
return panel;
}
@Override
public void applyEditorTo(BlazeAndroidRunConfigurationState state) {
BlazeAndroidBinaryRunConfigurationState configState =
(BlazeAndroidBinaryRunConfigurationState) state;
configState.setUserId((Integer) userIdField.getValue());
if (launchDefaultButton.isSelected()) {
configState.setMode(BlazeAndroidBinaryRunConfigurationState.LAUNCH_DEFAULT_ACTIVITY);
} else if (launchCustomButton.isSelected()) {
configState.setMode(BlazeAndroidBinaryRunConfigurationState.LAUNCH_SPECIFIC_ACTIVITY);
configState.setActivityClass(activityField.getChildComponent().getText());
} else {
configState.setMode(BlazeAndroidBinaryRunConfigurationState.DO_NOTHING);
}
configState.setMobileInstall(mobileInstallCheckBox.isSelected());
configState.setUseSplitApksIfPossible(splitApksCheckBox.isSelected());
configState.setInstantRun(instantRunCheckBox.isSelected());
configState.setUseWorkProfileIfPresent(useWorkProfileIfPresentCheckBox.isSelected());
}
private void createUIComponents() {
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() {
createUIComponents();
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));
mobileInstallCheckBox = new JCheckBox();
mobileInstallCheckBox.setText(" Use mobile-install");
panel.add(
mobileInstallCheckBox,
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));
instantRunCheckBox = new JCheckBox();
instantRunCheckBox.setText(" Use InstantRun");
panel.add(
instantRunCheckBox,
new GridConstraints(
2,
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);
}
}
}