diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java index 655c038e1..7ee3def57 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java @@ -1078,7 +1078,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener /** * Performs the installation of an application whose package has been uploaded on the device. - *

Before doing it, if the application is already running on the device, it is killed. + * * @param launchInfo the {@link DelayedLaunchInfo}. * @param remotePath the path of the application package in the device tmp folder. * @param device the device on which to install the application. @@ -1088,12 +1088,6 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener */ private String doInstall(DelayedLaunchInfo launchInfo, final String remotePath, final IDevice device, boolean reinstall) throws IOException { - // kill running application - Client application = device.getClient(launchInfo.getPackageName()); - if (application != null) { - application.kill(); - } - InstallReceiver receiver = new InstallReceiver(); try { String cmd = String.format( diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java index 9f12b16e4..4fa270e4e 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java @@ -23,6 +23,7 @@ import com.android.ide.eclipse.adt.project.ProjectHelper; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestParser; import com.android.ide.eclipse.common.project.BaseProjectHelper; +import com.android.ide.eclipse.common.project.AndroidManifestParser.Activity; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; @@ -247,7 +248,7 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate { activityName = getActivityName(configuration); // Get the full activity list and make sure the one we got matches. - String[] activities = manifestParser.getActivities(); + Activity[] activities = manifestParser.getActivities(); // first we check that there are, in fact, activities. if (activities.length == 0) { @@ -261,8 +262,11 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate { // if the activity we got is null, we look for the default one. AdtPlugin.printErrorToConsole(project, "No activity specified! Getting the launcher activity."); - activityName = manifestParser.getLauncherActivity(); - + Activity launcherActivity = manifestParser.getLauncherActivity(); + if (launcherActivity != null) { + activityName = launcherActivity.getName(); + } + // if there's no default activity. We revert to a sync-only launch. if (activityName == null) { revertToNoActionLaunch(project, config); @@ -271,8 +275,8 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate { // check the one we got from the config matches any from the list boolean match = false; - for (String a : activities) { - if (a != null && a.equals(activityName)) { + for (Activity a : activities) { + if (a != null && a.getName().equals(activityName)) { match = true; break; } @@ -282,7 +286,10 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate { if (match == false) { AdtPlugin.printErrorToConsole(project, "The specified activity does not exist! Getting the launcher activity."); - activityName = manifestParser.getLauncherActivity(); + Activity launcherActivity = manifestParser.getLauncherActivity(); + if (launcherActivity != null) { + activityName = launcherActivity.getName(); + } // if there's no default activity. We revert to a sync-only launch. if (activityName == null) { @@ -291,7 +298,10 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate { } } } else if (config.mLaunchAction == ACTION_DEFAULT) { - activityName = manifestParser.getLauncherActivity(); + Activity launcherActivity = manifestParser.getLauncherActivity(); + if (launcherActivity != null) { + activityName = launcherActivity.getName(); + } // if there's no default activity. We revert to a sync-only launch. if (activityName == null) { diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java index 91bd21cb7..a32c2ee0e 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/MainLaunchConfigTab.java @@ -20,6 +20,7 @@ import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.common.project.AndroidManifestParser; import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.ide.eclipse.common.project.ProjectChooserHelper; +import com.android.ide.eclipse.common.project.AndroidManifestParser.Activity; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; @@ -50,6 +51,8 @@ import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Text; +import java.util.ArrayList; + /** * Class for the main launch configuration tab. */ @@ -66,7 +69,7 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab { private Button mProjButton; private Combo mActivityCombo; - private String[] mActivities; + private final ArrayList mActivities = new ArrayList(); private WidgetListener mListener = new WidgetListener(); @@ -214,8 +217,9 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab { // add the activity int selection = mActivityCombo.getSelectionIndex(); - if (mActivities != null && selection >=0 && selection < mActivities.length) { - configuration.setAttribute(LaunchConfigDelegate.ATTR_ACTIVITY, mActivities[selection]); + if (mActivities != null && selection >=0 && selection < mActivities.size()) { + configuration.setAttribute(LaunchConfigDelegate.ATTR_ACTIVITY, + mActivities.get(selection).getName()); } // link the project and the launch config. @@ -349,11 +353,11 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab { mActivityCombo.setEnabled(true); if (activityName == null || activityName.equals(EMPTY_STRING)) { mActivityCombo.clearSelection(); - } else if (mActivities != null && mActivities.length > 0) { + } else if (mActivities != null && mActivities.size() > 0) { // look for the name of the activity in the combo. boolean found = false; - for (int i = 0 ; i < mActivities.length ; i++) { - if (activityName.equals(mActivities[i])) { + for (int i = 0 ; i < mActivities.size() ; i++) { + if (activityName.equals(mActivities.get(i).getName())) { found = true; mActivityCombo.select(i); break; @@ -404,17 +408,22 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab { BaseProjectHelper.getJavaProject(project), null /* errorListener */, true /* gatherData */, false /* markErrors */); if (manifestParser != null) { - mActivities = manifestParser.getActivities(); - + Activity[] activities = manifestParser.getActivities(); + + mActivities.clear(); mActivityCombo.removeAll(); - - if (mActivities.length > 0) { + + for (Activity activity : activities) { + if (activity.getExported() && activity.hasAction()) { + mActivities.add(activity); + mActivityCombo.add(activity.getName()); + } + } + + if (mActivities.size() > 0) { if (mLaunchAction == LaunchConfigDelegate.ACTION_ACTIVITY) { mActivityCombo.setEnabled(true); } - for (String s : mActivities) { - mActivityCombo.add(s); - } } else { mActivityCombo.setEnabled(false); } @@ -435,7 +444,7 @@ public class MainLaunchConfigTab extends AbstractLaunchConfigurationTab { // if we reach this point, either project is null, or we got an exception during // the parsing. In either case, we empty the activity list. mActivityCombo.removeAll(); - mActivities = null; + mActivities.clear(); } /** diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java index e26b31cc3..6e8ff47fe 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java @@ -27,6 +27,7 @@ import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestParser; +import com.android.ide.eclipse.common.project.AndroidManifestParser.Activity; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkConstants; import com.android.sdklib.project.ProjectProperties; @@ -859,6 +860,7 @@ public class NewProjectCreationPage extends WizardPage { } String packageName = null; + Activity activity = null; String activityName = null; int minSdkVersion = AndroidManifestParser.INVALID_MIN_SDK; try { @@ -866,11 +868,11 @@ public class NewProjectCreationPage extends WizardPage { minSdkVersion = manifestData.getApiLevelRequirement(); // try to get the first launcher activity. If none, just take the first activity. - activityName = manifestData.getLauncherActivity(); - if (activityName == null) { - String[] activities = manifestData.getActivities(); + activity = manifestData.getLauncherActivity(); + if (activity == null) { + Activity[] activities = manifestData.getActivities(); if (activities != null && activities.length > 0) { - activityName = activities[0]; + activity = activities[0]; } } } catch (Exception e) { @@ -881,7 +883,10 @@ public class NewProjectCreationPage extends WizardPage { mPackageNameField.setText(packageName); } - activityName = AndroidManifestParser.extractActivityName(activityName, packageName); + if (activity != null) { + activityName = AndroidManifestParser.extractActivityName(activity.getName(), + packageName); + } if (activityName != null && activityName.length() > 0) { mInternalActivityNameUpdate = true; @@ -1136,7 +1141,7 @@ public class NewProjectCreationPage extends WizardPage { MSG_ERROR); } - String[] activities = manifestData.getActivities(); + Activity[] activities = manifestData.getActivities(); if (activities == null || activities.length == 0) { // This is acceptable now as long as no activity needs to be created if (isCreateActivity()) { diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java index f853adacb..69982fc66 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java @@ -53,6 +53,7 @@ public class AndroidManifestParser { private final static String ATTRIBUTE_DEBUGGABLE = "debuggable"; //$NON-NLS-$ private final static String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion"; //$NON-NLS-$ private final static String ATTRIBUTE_TARGET_PACKAGE = "targetPackage"; //$NON-NLS-1$ + private final static String ATTRIBUTE_EXPORTED = "exported"; //$NON-NLS-1$ private final static String NODE_MANIFEST = "manifest"; //$NON-NLS-1$ private final static String NODE_APPLICATION = "application"; //$NON-NLS-1$ private final static String NODE_ACTIVITY = "activity"; //$NON-NLS-1$ @@ -84,7 +85,7 @@ public class AndroidManifestParser { private final String mName; private final String mTargetPackage; - public Instrumentation(String name, String targetPackage) { + Instrumentation(String name, String targetPackage) { mName = name; mTargetPackage = targetPackage; } @@ -104,6 +105,60 @@ public class AndroidManifestParser { } } + /** + * Activity info obtained from the manifest. + */ + public static class Activity { + private final String mName; + private final boolean mExported; + private boolean mHasAction = false; + private boolean mHasMainAction = false; + private boolean mHasLauncherCategory = false; + + public Activity(String name, boolean exported) { + mName = name; + mExported = exported; + } + + public String getName() { + return mName; + } + + public boolean getExported() { + return mExported; + } + + public boolean hasAction() { + return mHasAction; + } + + public boolean isHomeActivity() { + return mHasMainAction && mHasLauncherCategory; + } + + void setHasAction(boolean hasAction) { + mHasAction = hasAction; + } + + /** If the activity doesn't yet have a filter set for the launcher, this resets both + * flags. This is to handle multiple intent-filters where one could have the valid + * action, and another one of the valid category. + */ + void resetIntentFilter() { + if (isHomeActivity() == false) { + mHasMainAction = mHasLauncherCategory = false; + } + } + + void setHasMainAction(boolean hasMainAction) { + mHasMainAction = hasMainAction; + } + + void setHasLauncherCategory(boolean hasLauncherCategory) { + mHasLauncherCategory = hasLauncherCategory; + } + } + /** * XML error & data handler used when parsing the AndroidManifest.xml file. *

@@ -117,9 +172,9 @@ public class AndroidManifestParser { /** Application package */ private String mPackage; /** List of all activities */ - private final ArrayList mActivities = new ArrayList(); + private final ArrayList mActivities = new ArrayList(); /** Launcher activity */ - private String mLauncherActivity = null; + private Activity mLauncherActivity = null; /** list of process names declared by the manifest */ private Set mProcesses = null; /** debuggable attribute value. If null, the attribute is not present. */ @@ -139,9 +194,7 @@ public class AndroidManifestParser { private boolean mMarkErrors = false; private int mCurrentLevel = 0; private int mValidLevel = 0; - private boolean mFoundMainAction = false; - private boolean mFoundLauncherCategory = false; - private String mCurrentActivity = null; + private Activity mCurrentActivity = null; private Locator mLocator; /** @@ -173,8 +226,8 @@ public class AndroidManifestParser { * Returns the list of activities found in the manifest. * @return An array of fully qualified class names, or empty if no activity were found. */ - String[] getActivities() { - return mActivities.toArray(new String[mActivities.size()]); + Activity[] getActivities() { + return mActivities.toArray(new Activity[mActivities.size()]); } /** @@ -182,7 +235,7 @@ public class AndroidManifestParser { * up in the HOME screen. * @return the fully qualified name of a HOME activity or null if none were found. */ - String getLauncherActivity() { + Activity getLauncherActivity() { return mLauncherActivity; } @@ -314,27 +367,26 @@ public class AndroidManifestParser { case LEVEL_INTENT_FILTER: // only process this level if we are in an activity if (mCurrentActivity != null && NODE_INTENT.equals(localName)) { - // if we're at the intent level, lets reset some flag to - // be used when parsing the children - mFoundMainAction = false; - mFoundLauncherCategory = false; + mCurrentActivity.resetIntentFilter(); mValidLevel++; } break; case LEVEL_CATEGORY: - if (mCurrentActivity != null && mLauncherActivity == null) { + if (mCurrentActivity != null) { if (NODE_ACTION.equals(localName)) { // get the name attribute - if (ACTION_MAIN.equals( - getAttributeValue(attributes, ATTRIBUTE_NAME, - true /* hasNamespace */))) { - mFoundMainAction = true; + String action = getAttributeValue(attributes, ATTRIBUTE_NAME, + true /* hasNamespace */); + if (action != null) { + mCurrentActivity.setHasAction(true); + mCurrentActivity.setHasMainAction( + ACTION_MAIN.equals(action)); } } else if (NODE_CATEGORY.equals(localName)) { - if (CATEGORY_LAUNCHER.equals( - getAttributeValue(attributes, ATTRIBUTE_NAME, - true /* hasNamespace */))) { - mFoundLauncherCategory = true; + String category = getAttributeValue(attributes, ATTRIBUTE_NAME, + true /* hasNamespace */); + if (CATEGORY_LAUNCHER.equals(category)) { + mCurrentActivity.setHasLauncherCategory(true); } } @@ -378,8 +430,7 @@ public class AndroidManifestParser { case LEVEL_INTENT_FILTER: // if we found both a main action and a launcher category, this is our // launcher activity! - if (mCurrentActivity != null && - mFoundMainAction && mFoundLauncherCategory) { + if (mCurrentActivity != null && mCurrentActivity.isHomeActivity()) { mLauncherActivity = mCurrentActivity; } break; @@ -432,17 +483,23 @@ public class AndroidManifestParser { String activityName = getAttributeValue(attributes, ATTRIBUTE_NAME, true /* hasNamespace */); if (activityName != null) { - mCurrentActivity = combinePackageAndClassName(mPackage, activityName); + activityName = combinePackageAndClassName(mPackage, activityName); + + // get the exported flag. + String exportedStr = getAttributeValue(attributes, ATTRIBUTE_EXPORTED, true); + boolean exported = exportedStr == null || + exportedStr.toLowerCase().equals("true"); // $NON-NLS-1$ + mCurrentActivity = new Activity(activityName, exported); mActivities.add(mCurrentActivity); if (mMarkErrors) { - checkClass(mCurrentActivity, AndroidConstants.CLASS_ACTIVITY, + checkClass(activityName, AndroidConstants.CLASS_ACTIVITY, true /* testVisibility */); } } else { // no activity found! Aapt will output an error, // so we don't have to do anything - mCurrentActivity = activityName; + mCurrentActivity = null; } String processName = getAttributeValue(attributes, ATTRIBUTE_PROCESS, @@ -570,8 +627,8 @@ public class AndroidManifestParser { private static SAXParserFactory sParserFactory; private final String mJavaPackage; - private final String[] mActivities; - private final String mLauncherActivity; + private final Activity[] mActivities; + private final Activity mLauncherActivity; private final String[] mProcesses; private final Boolean mDebuggable; private final int mApiLevelRequirement; @@ -811,18 +868,18 @@ public class AndroidManifestParser { /** * Returns the list of activities found in the manifest. - * @return An array of fully qualified class names, or empty if no activity were found. + * @return An array of {@link Activity}, or empty if no activity were found. */ - public String[] getActivities() { + public Activity[] getActivities() { return mActivities; } /** * Returns the name of one activity found in the manifest, that is configured to show * up in the HOME screen. - * @return the fully qualified name of a HOME activity or null if none were found. + * @return The {@link Activity} representing a HOME activity or null if none were found. */ - public String getLauncherActivity() { + public Activity getLauncherActivity() { return mLauncherActivity; } @@ -880,8 +937,8 @@ public class AndroidManifestParser { * @param instrumentations the list of instrumentations parsed from the manifest. * @param libraries the list of libraries in use parsed from the manifest. */ - private AndroidManifestParser(String javaPackage, String[] activities, - String launcherActivity, String[] processes, Boolean debuggable, + private AndroidManifestParser(String javaPackage, Activity[] activities, + Activity launcherActivity, String[] processes, Boolean debuggable, int apiLevelRequirement, Instrumentation[] instrumentations, String[] libraries) { mJavaPackage = javaPackage; mActivities = activities;