diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-13 13:04:19 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-13 13:04:19 -0700 |
commit | 763ca7d5803d8088e192f4d226ff9e96820c7ace (patch) | |
tree | 0826a498b31dc1490e33d660350e81b56b8bc0a1 /eclipse | |
parent | 45aacc49a96e083eb68cda88ed0f7e262b7254d1 (diff) | |
download | sdk-763ca7d5803d8088e192f4d226ff9e96820c7ace.zip sdk-763ca7d5803d8088e192f4d226ff9e96820c7ace.tar.gz sdk-763ca7d5803d8088e192f4d226ff9e96820c7ace.tar.bz2 |
auto import from //branches/cupcake_rel/...@138607
Diffstat (limited to 'eclipse')
25 files changed, 1126 insertions, 571 deletions
diff --git a/eclipse/changes.txt b/eclipse/changes.txt index 781930c..8cd843e 100644 --- a/eclipse/changes.txt +++ b/eclipse/changes.txt @@ -1,4 +1,5 @@ 0.9.0 (work in progress) +- Projects now store generated Java files (R.java/Manifest.java and output from aidl) in a 'gen' source folder. - Support for the new Android SDK with support for multiple versions of the Android platform and for vendor supplied add-ons. * New Project Wizard lets you choose which platform/add-on to target. * Project properties (right click project in Package Explorer, then "Properties"), lets you edit project target. diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml index f86f5b2..39e6dd5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml +++ b/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml @@ -481,6 +481,12 @@ style="push" toolbarPath="android_project" tooltip="Opens a wizard to help create a new Android XML file"> + <enablement> + <objectState + name="projectNature" + value="com.android.ide.eclipse.adt.AndroidNature"> + </objectState> + </enablement> </action> <action class="com.android.ide.eclipse.adt.wizards.actions.NewProjectAction" diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java index 18d9745..f8a969e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java @@ -966,9 +966,10 @@ public class ApkBuilder extends BaseBuilder { * @param javaProject the javaProject object. * @param referencedJavaProjects the java projects that this project references. * @throws IOException + * @throws CoreException */ private void writeStandardResources(SignedJarBuilder jarBuilder, IJavaProject javaProject, - IJavaProject[] referencedJavaProjects) throws IOException { + IJavaProject[] referencedJavaProjects) throws IOException, CoreException { IWorkspace ws = ResourcesPlugin.getWorkspace(); IWorkspaceRoot wsRoot = ws.getRoot(); @@ -978,7 +979,9 @@ public class ApkBuilder extends BaseBuilder { writeStandardProjectResources(jarBuilder, javaProject, wsRoot, list); for (IJavaProject referencedJavaProject : referencedJavaProjects) { - writeStandardProjectResources(jarBuilder, referencedJavaProject, wsRoot, list); + if (referencedJavaProject.getProject().hasNature(AndroidConstants.NATURE)) { + writeStandardProjectResources(jarBuilder, referencedJavaProject, wsRoot, list); + } } } @@ -1067,7 +1070,9 @@ public class ApkBuilder extends BaseBuilder { } /** - * Returns the list of the output folders for the specified {@link IJavaProject} objects. + * Returns the list of the output folders for the specified {@link IJavaProject} objects, if + * they are Android projects. + * * @param referencedJavaProjects the java projects. * @return an array, always. Can be empty. * @throws CoreException @@ -1079,19 +1084,21 @@ public class ApkBuilder extends BaseBuilder { IWorkspaceRoot wsRoot = ws.getRoot(); for (IJavaProject javaProject : referencedJavaProjects) { - // get the output folder - IPath path = null; - try { - path = javaProject.getOutputLocation(); - } catch (JavaModelException e) { - continue; - } - - IResource outputResource = wsRoot.findMember(path); - if (outputResource != null && outputResource.getType() == IResource.FOLDER) { - String outputOsPath = outputResource.getLocation().toOSString(); - - list.add(outputOsPath); + if (javaProject.getProject().hasNature(AndroidConstants.NATURE)) { + // get the output folder + IPath path = null; + try { + path = javaProject.getOutputLocation(); + } catch (JavaModelException e) { + continue; + } + + IResource outputResource = wsRoot.findMember(path); + if (outputResource != null && outputResource.getType() == IResource.FOLDER) { + String outputOsPath = outputResource.getLocation().toOSString(); + + list.add(outputOsPath); + } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AMReceiver.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AMReceiver.java new file mode 100644 index 0000000..8fdcdf9 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AMReceiver.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.android.ide.eclipse.adt.launch; + +import com.android.ddmlib.IDevice; +import com.android.ddmlib.MultiLineReceiver; +import com.android.ide.eclipse.adt.AdtPlugin; + +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +/** + * Output receiver for am process (Activity Manager) + * + * Monitors adb output for am errors, and retries launch as appropriate. + */ +public class AMReceiver extends MultiLineReceiver { + + private static final int MAX_ATTEMPT_COUNT = 5; + private static final Pattern sAmErrorType = Pattern.compile("Error type (\\d+)"); //$NON-NLS-1$ + + private final DelayedLaunchInfo mLaunchInfo; + private final IDevice mDevice; + private final ILaunchController mLaunchController; + + /** + * Basic constructor. + * + * @param launchInfo the {@link DelayedLaunchInfo} associated with the am process. + * @param device the Android device on which the launch is done. + * @param launchController the {@link ILaunchController} that is managing the launch + */ + public AMReceiver(DelayedLaunchInfo launchInfo, IDevice device, + ILaunchController launchController) { + mLaunchInfo = launchInfo; + mDevice = device; + mLaunchController = launchController; + } + + /** + * Monitors the am process for error messages. If an error occurs, will reattempt launch up to + * <code>MAX_ATTEMPT_COUNT</code> times. + * + * @param lines a portion of the am output + * + * @see MultiLineReceiver#processNewLines(String[]) + */ + @Override + public void processNewLines(String[] lines) { + // first we check if one starts with error + ArrayList<String> array = new ArrayList<String>(); + boolean error = false; + boolean warning = false; + for (String s : lines) { + // ignore empty lines. + if (s.length() == 0) { + continue; + } + + // check for errors that output an error type, if the attempt count is still + // valid. If not the whole text will be output in the console + if (mLaunchInfo.getAttemptCount() < MAX_ATTEMPT_COUNT && + mLaunchInfo.isCancelled() == false) { + Matcher m = sAmErrorType.matcher(s); + if (m.matches()) { + // get the error type + int type = Integer.parseInt(m.group(1)); + + final int waitTime = 3; + String msg; + + switch (type) { + case 1: + /* Intended fall through */ + case 2: + msg = String.format( + "Device not ready. Waiting %1$d seconds before next attempt.", + waitTime); + break; + case 3: + msg = String.format( + "New package not yet registered with the system. Waiting %1$d seconds before next attempt.", + waitTime); + break; + default: + msg = String.format( + "Device not ready (%2$d). Waiting %1$d seconds before next attempt.", + waitTime, type); + break; + + } + + AdtPlugin.printToConsole(mLaunchInfo.getProject(), msg); + + // launch another thread, that waits a bit and attempts another launch + new Thread("Delayed Launch attempt") { + @Override + public void run() { + try { + sleep(waitTime * 1000); + } catch (InterruptedException e) { + // ignore + } + + mLaunchController.launchApp(mLaunchInfo, mDevice); + } + }.start(); + + // no need to parse the rest + return; + } + } + + // check for error if needed + if (error == false && s.startsWith("Error:")) { //$NON-NLS-1$ + error = true; + } + if (warning == false && s.startsWith("Warning:")) { //$NON-NLS-1$ + warning = true; + } + + // add the line to the list + array.add("ActivityManager: " + s); //$NON-NLS-1$ + } + + // then we display them in the console + if (warning || error) { + AdtPlugin.printErrorToConsole(mLaunchInfo.getProject(), array.toArray()); + } else { + AdtPlugin.printToConsole(mLaunchInfo.getProject(), array.toArray()); + } + + // if error then we cancel the launch, and remove the delayed info + if (error) { + mLaunchController.stopLaunch(mLaunchInfo); + } + } + + /** + * Returns true if launch has been cancelled + */ + public boolean isCancelled() { + return mLaunchInfo.isCancelled(); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/ActivityLaunchAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/ActivityLaunchAction.java new file mode 100644 index 0000000..1aa5a9a --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/ActivityLaunchAction.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.android.ide.eclipse.adt.launch; + +import com.android.ddmlib.IDevice; +import com.android.ide.eclipse.adt.AdtPlugin; + +import java.io.IOException; + +/** + * Launches the given activity + */ +public class ActivityLaunchAction implements IAndroidLaunchAction { + + private final String mActivity; + private final ILaunchController mLaunchController; + + /** + * Creates a ActivityLaunchAction + * + * @param activity fully qualified activity name to launch + * @param controller the {@link ILaunchController} that performs launch + */ + public ActivityLaunchAction(String activity, ILaunchController controller) { + mActivity = activity; + mLaunchController = controller; + } + + /** + * Launches the activity on targeted device + * + * @param info the {@link DelayedLaunchInfo} that contains launch details + * @param device the Android device to perform action on + * + * @see IAndroidLaunchAction#doLaunchAction(DelayedLaunchInfo, IDevice) + */ + public boolean doLaunchAction(DelayedLaunchInfo info, IDevice device) { + try { + String msg = String.format("Starting activity %1$s on device ", mActivity, + device); + AdtPlugin.printToConsole(info.getProject(), msg); + + // In debug mode, we need to add the info to the list of application monitoring + // client changes. + // increment launch attempt count, to handle retries and timeouts + info.incrementAttemptCount(); + + // now we actually launch the app. + device.executeShellCommand("am start" //$NON-NLS-1$ + + (info.isDebugMode() ? " -D" //$NON-NLS-1$ + : "") //$NON-NLS-1$ + + " -n " //$NON-NLS-1$ + + info.getPackageName() + "/" //$NON-NLS-1$ + + mActivity.replaceAll("\\$", "\\\\\\$"), //$NON-NLS-1$ //$NON-NLS-2$ + new AMReceiver(info, device, mLaunchController)); + + // if the app is not a debug app, we need to do some clean up, as + // the process is done! + if (info.isDebugMode() == false) { + // stop the launch object, since there's no debug, and it can't + // provide any control over the app + return false; + } + } catch (IOException e) { + // something went wrong trying to launch the app. + // lets stop the Launch + AdtPlugin.printErrorToConsole(info.getProject(), + String.format("Launch error: %s", e.getMessage())); + return false; + } + return true; + } + + /** + * Returns a description of the activity being launched + * + * @see IAndroidLaunchAction#getLaunchDescription() + */ + public String getLaunchDescription() { + return String.format("%1$s activity launch", mActivity); + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunch.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunch.java index 7029206..42927c2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunch.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunch.java @@ -26,7 +26,7 @@ import org.eclipse.debug.core.model.ISourceLocator; * Custom implementation of Launch to allow access to the LaunchManager * */ -class AndroidLaunch extends Launch { +public class AndroidLaunch extends Launch { /** * Basic constructor does nothing special diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java new file mode 100644 index 0000000..448cda6 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchConfiguration.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.android.ide.eclipse.adt.launch; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.ILaunchConfiguration; + +/** + * Launch configuration data. This stores the result of querying the + * {@link ILaunchConfiguration} so that it's only done once. + */ +public class AndroidLaunchConfiguration { + + /** + * Launch action. See {@link LaunchConfigDelegate#ACTION_DEFAULT}, + * {@link LaunchConfigDelegate#ACTION_ACTIVITY}, + * {@link LaunchConfigDelegate#ACTION_DO_NOTHING} + */ + public int mLaunchAction = LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION; + + public static final boolean AUTO_TARGET_MODE = true; + + /** + * Target selection mode. + * <ul> + * <li><code>true</code>: automatic mode, see {@link #AUTO_TARGET_MODE}</li> + * <li><code>false</code>: manual mode</li> + * </ul> + */ + public boolean mTargetMode = LaunchConfigDelegate.DEFAULT_TARGET_MODE; + + /** + * Indicates whether the emulator should be called with -wipe-data + */ + public boolean mWipeData = LaunchConfigDelegate.DEFAULT_WIPE_DATA; + + /** + * Indicates whether the emulator should be called with -no-boot-anim + */ + public boolean mNoBootAnim = LaunchConfigDelegate.DEFAULT_NO_BOOT_ANIM; + + /** + * AVD Name. + */ + public String mAvdName = null; + + public String mNetworkSpeed = EmulatorConfigTab.getSpeed( + LaunchConfigDelegate.DEFAULT_SPEED); + public String mNetworkDelay = EmulatorConfigTab.getDelay( + LaunchConfigDelegate.DEFAULT_DELAY); + + /** + * Optional custom command line parameter to launch the emulator + */ + public String mEmulatorCommandLine; + + /** + * Initialized the structure from an ILaunchConfiguration object. + * @param config + */ + public void set(ILaunchConfiguration config) { + try { + mLaunchAction = config.getAttribute(LaunchConfigDelegate.ATTR_LAUNCH_ACTION, + mLaunchAction); + } catch (CoreException e1) { + // nothing to be done here, we'll use the default value + } + + try { + mTargetMode = config.getAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE, + mTargetMode); + } catch (CoreException e) { + // nothing to be done here, we'll use the default value + } + + try { + mAvdName = config.getAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, mAvdName); + } catch (CoreException e) { + // ignore + } + + int index = LaunchConfigDelegate.DEFAULT_SPEED; + try { + index = config.getAttribute(LaunchConfigDelegate.ATTR_SPEED, index); + } catch (CoreException e) { + // nothing to be done here, we'll use the default value + } + mNetworkSpeed = EmulatorConfigTab.getSpeed(index); + + index = LaunchConfigDelegate.DEFAULT_DELAY; + try { + index = config.getAttribute(LaunchConfigDelegate.ATTR_DELAY, index); + } catch (CoreException e) { + // nothing to be done here, we'll use the default value + } + mNetworkDelay = EmulatorConfigTab.getDelay(index); + + try { + mEmulatorCommandLine = config.getAttribute( + LaunchConfigDelegate.ATTR_COMMANDLINE, ""); //$NON-NLS-1$ + } catch (CoreException e) { + // lets not do anything here, we'll use the default value + } + + try { + mWipeData = config.getAttribute(LaunchConfigDelegate.ATTR_WIPE_DATA, mWipeData); + } catch (CoreException e) { + // nothing to be done here, we'll use the default value + } + + try { + mNoBootAnim = config.getAttribute(LaunchConfigDelegate.ATTR_NO_BOOT_ANIM, + mNoBootAnim); + } catch (CoreException e) { + // nothing to be done here, we'll use the default value + } + } +} + diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java index 111d6b3..88ee8b6 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/AndroidLaunchController.java @@ -17,17 +17,19 @@ package com.android.ide.eclipse.adt.launch; import com.android.ddmlib.AndroidDebugBridge; +import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; +import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener; +import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; import com.android.ddmlib.Client; import com.android.ddmlib.ClientData; import com.android.ddmlib.Device; +import com.android.ddmlib.IDevice; import com.android.ddmlib.Log; import com.android.ddmlib.MultiLineReceiver; import com.android.ddmlib.SyncService; -import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; -import com.android.ddmlib.AndroidDebugBridge.IDebugBridgeChangeListener; -import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; import com.android.ddmlib.SyncService.SyncResult; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.launch.DelayedLaunchInfo.InstallRetryMode; import com.android.ide.eclipse.adt.launch.DeviceChooserDialog.DeviceChooserResponse; import com.android.ide.eclipse.adt.project.ProjectHelper; import com.android.ide.eclipse.adt.sdk.Sdk; @@ -72,7 +74,7 @@ import java.util.regex.Pattern; * it. */ public final class AndroidLaunchController implements IDebugBridgeChangeListener, - IDeviceChangeListener, IClientChangeListener { + IDeviceChangeListener, IClientChangeListener, ILaunchController { private static final String FLAG_AVD = "-avd"; //$NON-NLS-1$ private static final String FLAG_NETDELAY = "-netdelay"; //$NON-NLS-1$ @@ -80,83 +82,15 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener private static final String FLAG_WIPE_DATA = "-wipe-data"; //$NON-NLS-1$ private static final String FLAG_NO_BOOT_ANIM = "-no-boot-anim"; //$NON-NLS-1$ - private static final int MAX_ATTEMPT_COUNT = 5; - - private final static Pattern sAmErrorType = Pattern.compile("Error type (\\d+)"); //$NON-NLS-1$ - - /** - * A delayed launch waiting for a device to be present or ready before the - * application is launched. - */ - static final class DelayedLaunchInfo { - /** The device on which to launch the app */ - Device mDevice = null; - - /** The eclipse project */ - IProject mProject; - - /** Package name */ - String mPackageName; - - /** fully qualified name of the activity */ - String mActivity; - - /** IFile to the package (.apk) file */ - IFile mPackageFile; - - /** Debuggable attribute of the manifest file. */ - Boolean mDebuggable = null; - - /** Required ApiVersionNumber by the app. 0 means no requirements */ - int mRequiredApiVersionNumber = 0; - - InstallRetryMode mRetryMode = InstallRetryMode.NEVER; - - /** - * Launch action. See {@link LaunchConfigDelegate#ACTION_DEFAULT}, - * {@link LaunchConfigDelegate#ACTION_ACTIVITY}, - * {@link LaunchConfigDelegate#ACTION_DO_NOTHING} - */ - int mLaunchAction; - - /** the launch object */ - AndroidLaunch mLaunch; - - /** the monitor object */ - IProgressMonitor mMonitor; - - /** debug mode flag */ - boolean mDebugMode; - - int mAttemptCount = 0; - - boolean mCancelled = false; - - /** Basic constructor with activity and package info. */ - private DelayedLaunchInfo(IProject project, String packageName, String activity, - IFile pack, Boolean debuggable, int requiredApiVersionNumber, int launchAction, - AndroidLaunch launch, IProgressMonitor monitor) { - mProject = project; - mPackageName = packageName; - mActivity = activity; - mPackageFile = pack; - mLaunchAction = launchAction; - mLaunch = launch; - mMonitor = monitor; - mDebuggable = debuggable; - mRequiredApiVersionNumber = requiredApiVersionNumber; - } - } - /** * Map to store {@link ILaunchConfiguration} objects that must be launched as simple connection * to running application. The integer is the port on which to connect. * <b>ALL ACCESS MUST BE INSIDE A <code>synchronized (sListLock)</code> block!</b> */ - private final static HashMap<ILaunchConfiguration, Integer> sRunningAppMap = + private static final HashMap<ILaunchConfiguration, Integer> sRunningAppMap = new HashMap<ILaunchConfiguration, Integer>(); - private final static Object sListLock = sRunningAppMap; + private static final Object sListLock = sRunningAppMap; /** * List of {@link DelayedLaunchInfo} waiting for an emulator to connect. @@ -190,243 +124,15 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener /** static instance for singleton */ private static AndroidLaunchController sThis = new AndroidLaunchController(); - enum InstallRetryMode { - NEVER, ALWAYS, PROMPT; - } - - /** - * Launch configuration data. This stores the result of querying the - * {@link ILaunchConfiguration} so that it's only done once. - */ - static final class AndroidLaunchConfiguration { - - /** - * Launch action. See {@link LaunchConfigDelegate#ACTION_DEFAULT}, - * {@link LaunchConfigDelegate#ACTION_ACTIVITY}, - * {@link LaunchConfigDelegate#ACTION_DO_NOTHING} - */ - public int mLaunchAction = LaunchConfigDelegate.DEFAULT_LAUNCH_ACTION; - - public static final boolean AUTO_TARGET_MODE = true; - - /** - * Target selection mode. - * <ul> - * <li><code>true</code>: automatic mode, see {@link #AUTO_TARGET_MODE}</li> - * <li><code>false</code>: manual mode</li> - * </ul> - */ - public boolean mTargetMode = LaunchConfigDelegate.DEFAULT_TARGET_MODE; - - /** - * Indicates whether the emulator should be called with -wipe-data - */ - public boolean mWipeData = LaunchConfigDelegate.DEFAULT_WIPE_DATA; - - /** - * Indicates whether the emulator should be called with -no-boot-anim - */ - public boolean mNoBootAnim = LaunchConfigDelegate.DEFAULT_NO_BOOT_ANIM; - - /** - * AVD Name. - */ - public String mAvdName = null; - - public String mNetworkSpeed = EmulatorConfigTab.getSpeed( - LaunchConfigDelegate.DEFAULT_SPEED); - public String mNetworkDelay = EmulatorConfigTab.getDelay( - LaunchConfigDelegate.DEFAULT_DELAY); - - /** - * Optional custom command line parameter to launch the emulator - */ - public String mEmulatorCommandLine; - - /** - * Initialized the structure from an ILaunchConfiguration object. - * @param config - */ - public void set(ILaunchConfiguration config) { - try { - mLaunchAction = config.getAttribute(LaunchConfigDelegate.ATTR_LAUNCH_ACTION, - mLaunchAction); - } catch (CoreException e1) { - // nothing to be done here, we'll use the default value - } - - try { - mTargetMode = config.getAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE, - mTargetMode); - } catch (CoreException e) { - // nothing to be done here, we'll use the default value - } - - try { - mAvdName = config.getAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, mAvdName); - } catch (CoreException e) { - } - - int index = LaunchConfigDelegate.DEFAULT_SPEED; - try { - index = config.getAttribute(LaunchConfigDelegate.ATTR_SPEED, index); - } catch (CoreException e) { - // nothing to be done here, we'll use the default value - } - mNetworkSpeed = EmulatorConfigTab.getSpeed(index); - - index = LaunchConfigDelegate.DEFAULT_DELAY; - try { - index = config.getAttribute(LaunchConfigDelegate.ATTR_DELAY, index); - } catch (CoreException e) { - // nothing to be done here, we'll use the default value - } - mNetworkDelay = EmulatorConfigTab.getDelay(index); - - try { - mEmulatorCommandLine = config.getAttribute( - LaunchConfigDelegate.ATTR_COMMANDLINE, ""); //$NON-NLS-1$ - } catch (CoreException e) { - // lets not do anything here, we'll use the default value - } - - try { - mWipeData = config.getAttribute(LaunchConfigDelegate.ATTR_WIPE_DATA, mWipeData); - } catch (CoreException e) { - // nothing to be done here, we'll use the default value - } - - try { - mNoBootAnim = config.getAttribute(LaunchConfigDelegate.ATTR_NO_BOOT_ANIM, - mNoBootAnim); - } catch (CoreException e) { - // nothing to be done here, we'll use the default value - } - } - } - /** - * Output receiver for am process (activity Manager); - */ - private final class AMReceiver extends MultiLineReceiver { - private DelayedLaunchInfo mLaunchInfo; - private Device mDevice; - - /** - * Basic constructor. - * @param launchInfo The launch info associated with the am process. - * @param device The device on which the launch is done. - */ - public AMReceiver(DelayedLaunchInfo launchInfo, Device device) { - mLaunchInfo = launchInfo; - mDevice = device; - } - - @Override - public void processNewLines(String[] lines) { - // first we check if one starts with error - ArrayList<String> array = new ArrayList<String>(); - boolean error = false; - boolean warning = false; - for (String s : lines) { - // ignore empty lines. - if (s.length() == 0) { - continue; - } - - // check for errors that output an error type, if the attempt count is still - // valid. If not the whole text will be output in the console - if (mLaunchInfo.mAttemptCount < MAX_ATTEMPT_COUNT && - mLaunchInfo.mCancelled == false) { - Matcher m = sAmErrorType.matcher(s); - if (m.matches()) { - // get the error type - int type = Integer.parseInt(m.group(1)); - - final int waitTime = 3; - String msg; - - switch (type) { - case 1: - /* Intended fall through */ - case 2: - msg = String.format( - "Device not ready. Waiting %1$d seconds before next attempt.", - waitTime); - break; - case 3: - msg = String.format( - "New package not yet registered with the system. Waiting %1$d seconds before next attempt.", - waitTime); - break; - default: - msg = String.format( - "Device not ready (%2$d). Waiting %1$d seconds before next attempt.", - waitTime, type); - break; - - } - - AdtPlugin.printToConsole(mLaunchInfo.mProject, msg); - - // launch another thread, that waits a bit and attempts another launch - new Thread("Delayed Launch attempt") { - @Override - public void run() { - try { - sleep(waitTime * 1000); - } catch (InterruptedException e) { - } - - launchApp(mLaunchInfo, mDevice); - } - }.start(); - - // no need to parse the rest - return; - } - } - - // check for error if needed - if (error == false && s.startsWith("Error:")) { //$NON-NLS-1$ - error = true; - } - if (warning == false && s.startsWith("Warning:")) { //$NON-NLS-1$ - warning = true; - } - - // add the line to the list - array.add("ActivityManager: " + s); //$NON-NLS-1$ - } - - // then we display them in the console - if (warning || error) { - AdtPlugin.printErrorToConsole(mLaunchInfo.mProject, array.toArray()); - } else { - AdtPlugin.printToConsole(mLaunchInfo.mProject, array.toArray()); - } - - // if error then we cancel the launch, and remove the delayed info - if (error) { - mLaunchInfo.mLaunch.stopLaunch(); - synchronized (sListLock) { - mWaitingForReadyEmulatorList.remove(mLaunchInfo); - } - } - } - - public boolean isCancelled() { - return false; - } - } /** * Output receiver for "pm install package.apk" command line. */ - private final static class InstallReceiver extends MultiLineReceiver { + private static final class InstallReceiver extends MultiLineReceiver { - private final static String SUCCESS_OUTPUT = "Success"; //$NON-NLS-1$ - private final static Pattern FAILURE_PATTERN = Pattern.compile("Failure\\s+\\[(.*)\\]"); //$NON-NLS-1$ + private static final String SUCCESS_OUTPUT = "Success"; //$NON-NLS-1$ + private static final Pattern FAILURE_PATTERN = Pattern.compile("Failure\\s+\\[(.*)\\]"); //$NON-NLS-1$ private String mSuccess = null; @@ -533,7 +239,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener LaunchConfigDelegate.DEFAULT_TARGET_MODE); // default AVD: None - wc.setAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, (String)null); + wc.setAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, (String) null); // set the default network speed wc.setAttribute(LaunchConfigDelegate.ATTR_SPEED, @@ -602,30 +308,24 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener * @param apk the resource to the apk to launch. * @param debuggable the debuggable value of the app, or null if not set. * @param requiredApiVersionNumber the api version required by the app, or -1 if none. - * @param activity the class to provide to am to launch + * @param launchAction the action to perform after app sync * @param config the launch configuration * @param launch the launch object */ public void launch(final IProject project, String mode, IFile apk, - String packageName, Boolean debuggable, int requiredApiVersionNumber, String activity, - final AndroidLaunchConfiguration config, final AndroidLaunch launch, - IProgressMonitor monitor) { + String packageName, Boolean debuggable, int requiredApiVersionNumber, + final IAndroidLaunchAction launchAction, final AndroidLaunchConfiguration config, + final AndroidLaunch launch, IProgressMonitor monitor) { - String message; - if (config.mLaunchAction == LaunchConfigDelegate.ACTION_DO_NOTHING) { - message = String.format("Only Syncing Application Package"); - } else { - message = String.format("Launching: %1$s", activity); - } + String message = String.format("Performing %1$s", launchAction.getLaunchDescription()); AdtPlugin.printToConsole(project, message); // create the launch info final DelayedLaunchInfo launchInfo = new DelayedLaunchInfo(project, packageName, - activity, apk, debuggable, requiredApiVersionNumber, config.mLaunchAction, - launch, monitor); + launchAction, apk, debuggable, requiredApiVersionNumber, launch, monitor); // set the debug mode - launchInfo.mDebugMode = mode.equals(ILaunchManager.DEBUG_MODE); + launchInfo.setDebugMode(mode.equals(ILaunchManager.DEBUG_MODE)); // get the SDK Sdk currentSdk = Sdk.getCurrent(); @@ -657,7 +357,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener if (config.mTargetMode == AndroidLaunchConfiguration.AUTO_TARGET_MODE) { // if we are in automatic target mode, we need to find the current devices - Device[] devices = AndroidDebugBridge.getBridge().getDevices(); + IDevice[] devices = AndroidDebugBridge.getBridge().getDevices(); // first check if we have a preferred AVD name, and if it actually exists, and is valid // (ie able to run the project). @@ -677,7 +377,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener if (preferredAvd != null) { // look for a matching device - for (Device d : devices) { + for (IDevice d : devices) { String deviceAvd = d.getAvdName(); if (deviceAvd != null && deviceAvd.equals(config.mAvdName)) { response.setDeviceToUse(d); @@ -704,12 +404,12 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener } // no (valid) preferred AVD? look for one. - HashMap<Device, AvdInfo> compatibleRunningAvds = new HashMap<Device, AvdInfo>(); + HashMap<IDevice, AvdInfo> compatibleRunningAvds = new HashMap<IDevice, AvdInfo>(); boolean hasDevice = false; // if there's 1+ device running, we may force manual mode, // as we cannot always detect proper compatibility with // devices. This is the case if the project target is not // a standard platform - for (Device d : devices) { + for (IDevice d : devices) { String deviceAvd = d.getAvdName(); if (deviceAvd != null) { // physical devices return null. AvdInfo info = avdManager.getAvd(deviceAvd); @@ -770,11 +470,11 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener AdtPlugin.printErrorToConsole(project, String.format( "Failed to find a AVD compatible with target '%1$s'. Launch aborted.", projectTarget.getName())); - launch.stopLaunch(); + stopLaunch(launchInfo); return; } } else if (hasDevice == false && compatibleRunningAvds.size() == 1) { - Entry<Device, AvdInfo> e = compatibleRunningAvds.entrySet().iterator().next(); + Entry<IDevice, AvdInfo> e = compatibleRunningAvds.entrySet().iterator().next(); response.setDeviceToUse(e.getKey()); // get the AvdInfo, if null, the device is a physical device. @@ -810,13 +510,13 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener // or the AVD to launch. DeviceChooserDialog dialog = new DeviceChooserDialog( AdtPlugin.getDisplay().getActiveShell(), - response, launchInfo.mPackageName, projectTarget); + response, launchInfo.getPackageName(), projectTarget); if (dialog.open() == Dialog.OK) { AndroidLaunchController.this.continueLaunch(response, project, launch, launchInfo, config); } else { AdtPlugin.printErrorToConsole(project, "Launch canceled!"); - launch.stopLaunch(); + stopLaunch(launchInfo); return; } } catch (Exception e) { @@ -830,7 +530,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener } AdtPlugin.printErrorToConsole(project, String.format("Error during launch: %s", msg)); - launch.stopLaunch(); + stopLaunch(launchInfo); } } }); @@ -871,15 +571,15 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener // stop the launch and return mWaitingForEmulatorLaunches.remove(launchInfo); AdtPlugin.printErrorToConsole(project, "Launch canceled!"); - launch.stopLaunch(); + stopLaunch(launchInfo); return; } return; } } else if (response.getDeviceToUse() != null) { - launchInfo.mDevice = response.getDeviceToUse(); - simpleLaunch(launchInfo, launchInfo.mDevice); + launchInfo.setDevice(response.getDeviceToUse()); + simpleLaunch(launchInfo, launchInfo.getDevice()); } } }.start(); @@ -932,12 +632,12 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener * "release" mode instead of "debug"</li> * <ul> */ - private boolean checkBuildInfo(DelayedLaunchInfo launchInfo, Device device) { + private boolean checkBuildInfo(DelayedLaunchInfo launchInfo, IDevice device) { if (device != null) { // check the app required API level versus the target device API level - String deviceApiVersionName = device.getProperty(Device.PROP_BUILD_VERSION); - String value = device.getProperty(Device.PROP_BUILD_VERSION_NUMBER); + String deviceApiVersionName = device.getProperty(IDevice.PROP_BUILD_VERSION); + String value = device.getProperty(IDevice.PROP_BUILD_VERSION_NUMBER); int deviceApiVersionNumber = 0; try { deviceApiVersionNumber = Integer.parseInt(value); @@ -945,30 +645,30 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener // pass, we'll keep the deviceVersionNumber value at 0. } - if (launchInfo.mRequiredApiVersionNumber == 0) { + if (launchInfo.getRequiredApiVersionNumber() == 0) { // warn the API level requirement is not set. - AdtPlugin.printErrorToConsole(launchInfo.mProject, + AdtPlugin.printErrorToConsole(launchInfo.getProject(), "WARNING: Application does not specify an API level requirement!"); // and display the target device API level (if known) if (deviceApiVersionName == null || deviceApiVersionNumber == 0) { - AdtPlugin.printErrorToConsole(launchInfo.mProject, + AdtPlugin.printErrorToConsole(launchInfo.getProject(), "WARNING: Unknown device API version!"); } else { - AdtPlugin.printErrorToConsole(launchInfo.mProject, String.format( + AdtPlugin.printErrorToConsole(launchInfo.getProject(), String.format( "Device API version is %1$d (Android %2$s)", deviceApiVersionNumber, deviceApiVersionName)); } } else { // app requires a specific API level if (deviceApiVersionName == null || deviceApiVersionNumber == 0) { - AdtPlugin.printToConsole(launchInfo.mProject, + AdtPlugin.printToConsole(launchInfo.getProject(), "WARNING: Unknown device API version!"); - } else if (deviceApiVersionNumber < launchInfo.mRequiredApiVersionNumber) { + } else if (deviceApiVersionNumber < launchInfo.getRequiredApiVersionNumber()) { String msg = String.format( "ERROR: Application requires API version %1$d. Device API version is %2$d (Android %3$s).", - launchInfo.mRequiredApiVersionNumber, deviceApiVersionNumber, + launchInfo.getRequiredApiVersionNumber(), deviceApiVersionNumber, deviceApiVersionName); - AdtPlugin.printErrorToConsole(launchInfo.mProject, msg); + AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg); // abort the launch return false; @@ -976,33 +676,33 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener } // now checks that the device/app can be debugged (if needed) - if (device.isEmulator() == false && launchInfo.mDebugMode) { - String debuggableDevice = device.getProperty(Device.PROP_DEBUGGABLE); + if (device.isEmulator() == false && launchInfo.isDebugMode()) { + String debuggableDevice = device.getProperty(IDevice.PROP_DEBUGGABLE); if (debuggableDevice != null && debuggableDevice.equals("0")) { //$NON-NLS-1$ // the device is "secure" and requires apps to declare themselves as debuggable! - if (launchInfo.mDebuggable == null) { + if (launchInfo.getDebuggable() == null) { String message1 = String.format( "Device '%1$s' requires that applications explicitely declare themselves as debuggable in their manifest.", device.getSerialNumber()); String message2 = String.format("Application '%1$s' does not have the attribute 'debuggable' set to TRUE in its manifest and cannot be debugged.", - launchInfo.mPackageName); - AdtPlugin.printErrorToConsole(launchInfo.mProject, message1, message2); + launchInfo.getPackageName()); + AdtPlugin.printErrorToConsole(launchInfo.getProject(), message1, message2); // because am -D does not check for ro.debuggable and the // 'debuggable' attribute, it is important we do not use the -D option // in this case or the app will wait for a debugger forever and never // really launch. - launchInfo.mDebugMode = false; - } else if (launchInfo.mDebuggable == Boolean.FALSE) { + launchInfo.setDebugMode(false); + } else if (launchInfo.getDebuggable() == Boolean.FALSE) { String message = String.format("Application '%1$s' has its 'debuggable' attribute set to FALSE and cannot be debugged.", - launchInfo.mPackageName); - AdtPlugin.printErrorToConsole(launchInfo.mProject, message); + launchInfo.getPackageName()); + AdtPlugin.printErrorToConsole(launchInfo.getProject(), message); // because am -D does not check for ro.debuggable and the // 'debuggable' attribute, it is important we do not use the -D option // in this case or the app will wait for a debugger forever and never // really launch. - launchInfo.mDebugMode = false; + launchInfo.setDebugMode(false); } } } @@ -1019,18 +719,18 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener * @param device * @return true if succeed */ - private boolean simpleLaunch(DelayedLaunchInfo launchInfo, Device device) { + private boolean simpleLaunch(DelayedLaunchInfo launchInfo, IDevice device) { // API level check if (checkBuildInfo(launchInfo, device) == false) { - AdtPlugin.printErrorToConsole(launchInfo.mProject, "Launch canceled!"); - launchInfo.mLaunch.stopLaunch(); + AdtPlugin.printErrorToConsole(launchInfo.getProject(), "Launch canceled!"); + stopLaunch(launchInfo); return false; } // sync the app if (syncApp(launchInfo, device) == false) { - AdtPlugin.printErrorToConsole(launchInfo.mProject, "Launch canceled!"); - launchInfo.mLaunch.stopLaunch(); + AdtPlugin.printErrorToConsole(launchInfo.getProject(), "Launch canceled!"); + stopLaunch(launchInfo); return false; } @@ -1048,16 +748,16 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener * @param device the device on which to sync the application * @return true if the install succeeded. */ - private boolean syncApp(DelayedLaunchInfo launchInfo, Device device) { + private boolean syncApp(DelayedLaunchInfo launchInfo, IDevice device) { SyncService sync = device.getSyncService(); if (sync != null) { - IPath path = launchInfo.mPackageFile.getLocation(); + IPath path = launchInfo.getPackageFile().getLocation(); String message = String.format("Uploading %1$s onto device '%2$s'", path.lastSegment(), device.getSerialNumber()); - AdtPlugin.printToConsole(launchInfo.mProject, message); + AdtPlugin.printToConsole(launchInfo.getProject(), message); String osLocalPath = path.toOSString(); - String apkName = launchInfo.mPackageFile.getName(); + String apkName = launchInfo.getPackageFile().getName(); String remotePath = "/data/local/tmp/" + apkName; //$NON-NLS-1$ SyncResult result = sync.pushFile(osLocalPath, remotePath, @@ -1066,7 +766,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener if (result.getCode() != SyncService.RESULT_OK) { String msg = String.format("Failed to upload %1$s on '%2$s': %3$s", apkName, device.getSerialNumber(), result.getMessage()); - AdtPlugin.printErrorToConsole(launchInfo.mProject, msg); + AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg); return false; } @@ -1087,7 +787,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener } }); } catch (IOException e) { - AdtPlugin.printErrorToConsole(launchInfo.mProject, String.format( + AdtPlugin.printErrorToConsole(launchInfo.getProject(), String.format( "Failed to delete temporary package: %1$s", e.getMessage())); return false; } @@ -1097,8 +797,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener String msg = String.format( "Failed to upload %1$s on device '%2$s': Unable to open sync connection!", - launchInfo.mPackageFile.getName(), device.getSerialNumber()); - AdtPlugin.printErrorToConsole(launchInfo.mProject, msg); + launchInfo.getPackageFile().getName(), device.getSerialNumber()); + AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg); return false; } @@ -1110,10 +810,10 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener * @param device The device on which the launch is done. */ private boolean installPackage(DelayedLaunchInfo launchInfo, final String remotePath, - final Device device) { + final IDevice device) { - String message = String.format("Installing %1$s...", launchInfo.mPackageFile.getName()); - AdtPlugin.printToConsole(launchInfo.mProject, message); + String message = String.format("Installing %1$s...", launchInfo.getPackageFile().getName()); + AdtPlugin.printToConsole(launchInfo.getProject(), message); try { String result = doInstall(launchInfo, remotePath, device, false /* reinstall */); @@ -1141,10 +841,10 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener * @return <code>true<code> if success, <code>false</code> otherwise. * @throws IOException */ - private boolean checkInstallResult(String result, Device device, DelayedLaunchInfo launchInfo, + private boolean checkInstallResult(String result, IDevice device, DelayedLaunchInfo launchInfo, String remotePath, InstallRetryMode retryMode) throws IOException { if (result == null) { - AdtPlugin.printToConsole(launchInfo.mProject, "Success!"); + AdtPlugin.printToConsole(launchInfo.getProject(), "Success!"); return true; } else if (result.equals("INSTALL_FAILED_ALREADY_EXISTS")) { //$NON-NLS-1$ if (retryMode == InstallRetryMode.PROMPT) { @@ -1153,7 +853,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener if (prompt) { retryMode = InstallRetryMode.ALWAYS; } else { - AdtPlugin.printErrorToConsole(launchInfo.mProject, + AdtPlugin.printErrorToConsole(launchInfo.getProject(), "Installation error! The package already exists."); return false; } @@ -1178,35 +878,35 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener } */ - AdtPlugin.printToConsole(launchInfo.mProject, + AdtPlugin.printToConsole(launchInfo.getProject(), "Application already exists. Attempting to re-install instead..."); String res = doInstall(launchInfo, remotePath, device, true /* reinstall */); return checkInstallResult(res, device, launchInfo, remotePath, InstallRetryMode.NEVER); } - AdtPlugin.printErrorToConsole(launchInfo.mProject, + AdtPlugin.printErrorToConsole(launchInfo.getProject(), "Installation error! The package already exists."); } else if (result.equals("INSTALL_FAILED_INVALID_APK")) { //$NON-NLS-1$ - AdtPlugin.printErrorToConsole(launchInfo.mProject, + AdtPlugin.printErrorToConsole(launchInfo.getProject(), "Installation failed due to invalid APK file!", "Please check logcat output for more details."); } else if (result.equals("INSTALL_FAILED_INVALID_URI")) { //$NON-NLS-1$ - AdtPlugin.printErrorToConsole(launchInfo.mProject, + AdtPlugin.printErrorToConsole(launchInfo.getProject(), "Installation failed due to invalid URI!", "Please check logcat output for more details."); } else if (result.equals("INSTALL_FAILED_COULDNT_COPY")) { //$NON-NLS-1$ - AdtPlugin.printErrorToConsole(launchInfo.mProject, + AdtPlugin.printErrorToConsole(launchInfo.getProject(), String.format("Installation failed: Could not copy %1$s to its final location!", - launchInfo.mPackageFile.getName()), + launchInfo.getPackageFile().getName()), "Please check logcat output for more details."); } else if (result.equals("INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES")) { - AdtPlugin.printErrorToConsole(launchInfo.mProject, + AdtPlugin.printErrorToConsole(launchInfo.getProject(), "Re-installation failed due to different application signatures.", "You must perform a full uninstall of the application. WARNING: This will remove the application data!", - String.format("Please execute 'adb uninstall %1$s' in a shell.", launchInfo.mPackageName)); + String.format("Please execute 'adb uninstall %1$s' in a shell.", launchInfo.getPackageName())); } else { - AdtPlugin.printErrorToConsole(launchInfo.mProject, + AdtPlugin.printErrorToConsole(launchInfo.getProject(), String.format("Installation error: %1$s", result), "Please check logcat output for more details."); } @@ -1222,15 +922,15 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener * @throws IOException */ @SuppressWarnings("unused") - private String doUninstall(Device device, DelayedLaunchInfo launchInfo) throws IOException { + private String doUninstall(IDevice device, DelayedLaunchInfo launchInfo) throws IOException { InstallReceiver receiver = new InstallReceiver(); try { - device.executeShellCommand("pm uninstall " + launchInfo.mPackageName, //$NON-NLS-1$ + device.executeShellCommand("pm uninstall " + launchInfo.getPackageName(), //$NON-NLS-1$ receiver); } catch (IOException e) { String msg = String.format( - "Failed to uninstall %1$s: %2$s", launchInfo.mPackageName, e.getMessage()); - AdtPlugin.printErrorToConsole(launchInfo.mProject, msg); + "Failed to uninstall %1$s: %2$s", launchInfo.getPackageName(), e.getMessage()); + AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg); throw e; } @@ -1248,9 +948,9 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener * @throws IOException */ private String doInstall(DelayedLaunchInfo launchInfo, final String remotePath, - final Device device, boolean reinstall) throws IOException { + final IDevice device, boolean reinstall) throws IOException { // kill running application - Client application = device.getClient(launchInfo.mPackageName); + Client application = device.getClient(launchInfo.getPackageName()); if (application != null) { application.kill(); } @@ -1264,8 +964,9 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener } catch (IOException e) { String msg = String.format( "Failed to install %1$s on device '%2$s': %3$s", - launchInfo.mPackageFile.getName(), device.getSerialNumber(), e.getMessage()); - AdtPlugin.printErrorToConsole(launchInfo.mProject, msg); + launchInfo.getPackageFile().getName(), device.getSerialNumber(), + e.getMessage()); + AdtPlugin.printErrorToConsole(launchInfo.getProject(), msg); throw e; } @@ -1275,63 +976,29 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener /** * launches an application on a device or emulator * - * @param info the {@link DelayedLaunchInfo} that indicates the activity to launch + * @param info the {@link DelayedLaunchInfo} that indicates the launch action * @param device the device or emulator to launch the application on */ - private void launchApp(final DelayedLaunchInfo info, Device device) { - // if we're not supposed to do anything, just stop the Launch item and return; - if (info.mLaunchAction == LaunchConfigDelegate.ACTION_DO_NOTHING) { - String msg = String.format("%1$s installed on device", - info.mPackageFile.getFullPath().toOSString()); - AdtPlugin.printToConsole(info.mProject, msg, "Done!"); - info.mLaunch.stopLaunch(); - return; - } - try { - String msg = String.format("Starting activity %1$s on device ", info.mActivity, - info.mDevice); - AdtPlugin.printToConsole(info.mProject, msg); - - // In debug mode, we need to add the info to the list of application monitoring - // client changes. - if (info.mDebugMode) { - synchronized (sListLock) { - if (mWaitingForDebuggerApplications.contains(info) == false) { - mWaitingForDebuggerApplications.add(info); - } + public void launchApp(final DelayedLaunchInfo info, IDevice device) { + if (info.isDebugMode()) { + synchronized (sListLock) { + if (mWaitingForDebuggerApplications.contains(info) == false) { + mWaitingForDebuggerApplications.add(info); } } - - // increment launch attempt count, to handle retries and timeouts - info.mAttemptCount++; - - // now we actually launch the app. - device.executeShellCommand("am start" //$NON-NLS-1$ - + (info.mDebugMode ? " -D" //$NON-NLS-1$ - : "") //$NON-NLS-1$ - + " -n " //$NON-NLS-1$ - + info.mPackageName + "/" //$NON-NLS-1$ - + info.mActivity.replaceAll("\\$", "\\\\\\$"), //$NON-NLS-1$ //$NON-NLS-2$ - new AMReceiver(info, device)); - + } + if (info.getLaunchAction().doLaunchAction(info, device)) { // if the app is not a debug app, we need to do some clean up, as // the process is done! - if (info.mDebugMode == false) { + if (info.isDebugMode() == false) { // stop the launch object, since there's no debug, and it can't // provide any control over the app - info.mLaunch.stopLaunch(); + stopLaunch(info); } - } catch (IOException e) { - // something went wrong trying to launch the app. + } else { + // something went wrong or no further launch action needed // lets stop the Launch - AdtPlugin.printErrorToConsole(info.mProject, - String.format("Launch error: %s", e.getMessage())); - info.mLaunch.stopLaunch(); - - // and remove it from the list of app waiting for debuggers - synchronized (sListLock) { - mWaitingForDebuggerApplications.remove(info); - } + stopLaunch(info); } } @@ -1339,7 +1006,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener // split the custom command line in segments ArrayList<String> customArgs = new ArrayList<String>(); - boolean has_wipe_data = false; + boolean hasWipeData = false; if (config.mEmulatorCommandLine != null && config.mEmulatorCommandLine.length() > 0) { String[] segments = config.mEmulatorCommandLine.split("\\s+"); //$NON-NLS-1$ @@ -1347,17 +1014,17 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener for (String s : segments) { if (s.length() > 0) { customArgs.add(s); - if (!has_wipe_data && s.equals(FLAG_WIPE_DATA)) { - has_wipe_data = true; + if (!hasWipeData && s.equals(FLAG_WIPE_DATA)) { + hasWipeData = true; } } } } - boolean needs_wipe_data = config.mWipeData && !has_wipe_data; - if (needs_wipe_data) { + boolean needsWipeData = config.mWipeData && !hasWipeData; + if (needsWipeData) { if (!AdtPlugin.displayPrompt("Android Launch", "Are you sure you want to wipe all user data when starting this emulator?")) { - needs_wipe_data = false; + needsWipeData = false; } } @@ -1378,7 +1045,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener list.add(config.mNetworkDelay); } - if (needs_wipe_data) { + if (needsWipeData) { list.add(FLAG_WIPE_DATA); } @@ -1482,7 +1149,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener * @param monitor A Progress monitor * @see #connectRemoteDebugger(int, AndroidLaunch, IProgressMonitor) */ - public static void launchRemoteDebugger( final int debugPort, final AndroidLaunch androidLaunch, + public static void launchRemoteDebugger(final int debugPort, final AndroidLaunch androidLaunch, final IProgressMonitor monitor) { new Thread("Debugger connection") { //$NON-NLS-1$ @Override @@ -1507,26 +1174,18 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener */ public void bridgeChanged(AndroidDebugBridge bridge) { // The adb server has changed. We cancel any pending launches. - String message1 = "adb server change: cancelling '%1$s' launch!"; - String message2 = "adb server change: cancelling sync!"; + String message = "adb server change: cancelling '%1$s'!"; synchronized (sListLock) { for (DelayedLaunchInfo launchInfo : mWaitingForReadyEmulatorList) { - if (launchInfo.mLaunchAction == LaunchConfigDelegate.ACTION_DO_NOTHING) { - AdtPlugin.printErrorToConsole(launchInfo.mProject, message2); - } else { - AdtPlugin.printErrorToConsole(launchInfo.mProject, - String.format(message1, launchInfo.mActivity)); - } - launchInfo.mLaunch.stopLaunch(); + AdtPlugin.printErrorToConsole(launchInfo.getProject(), + String.format(message, launchInfo.getLaunchAction().getLaunchDescription())); + stopLaunch(launchInfo); } for (DelayedLaunchInfo launchInfo : mWaitingForDebuggerApplications) { - if (launchInfo.mLaunchAction == LaunchConfigDelegate.ACTION_DO_NOTHING) { - AdtPlugin.printErrorToConsole(launchInfo.mProject, message2); - } else { - AdtPlugin.printErrorToConsole(launchInfo.mProject, - String.format(message1, launchInfo.mActivity)); - } - launchInfo.mLaunch.stopLaunch(); + AdtPlugin.printErrorToConsole(launchInfo.getProject(), + String.format(message, + launchInfo.getLaunchAction().getLaunchDescription())); + stopLaunch(launchInfo); } mWaitingForReadyEmulatorList.clear(); @@ -1552,15 +1211,15 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener mWaitingForEmulatorLaunches.remove(0); // give the launch item its device for later use. - launchInfo.mDevice = device; + launchInfo.setDevice(device); // and move it to the other list mWaitingForReadyEmulatorList.add(launchInfo); // and tell the user about it - AdtPlugin.printToConsole(launchInfo.mProject, + AdtPlugin.printToConsole(launchInfo.getProject(), String.format("New emulator found: %1$s", device.getSerialNumber())); - AdtPlugin.printToConsole(launchInfo.mProject, + AdtPlugin.printToConsole(launchInfo.getProject(), String.format("Waiting for HOME ('%1$s') to be launched...", AdtPlugin.getDefault().getPreferenceStore().getString( AdtPlugin.PREFS_HOME_PACKAGE))); @@ -1579,25 +1238,25 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener @SuppressWarnings("unchecked") public void deviceDisconnected(Device device) { // any pending launch on this device must be canceled. - String message = "%1$s disconnected! Cancelling '%2$s' launch!"; + String message = "%1$s disconnected! Cancelling '%2$s'!"; synchronized (sListLock) { ArrayList<DelayedLaunchInfo> copyList = - (ArrayList<DelayedLaunchInfo>)mWaitingForReadyEmulatorList.clone(); + (ArrayList<DelayedLaunchInfo>) mWaitingForReadyEmulatorList.clone(); for (DelayedLaunchInfo launchInfo : copyList) { - if (launchInfo.mDevice == device) { - AdtPlugin.printErrorToConsole(launchInfo.mProject, - String.format(message, device.getSerialNumber(), launchInfo.mActivity)); - launchInfo.mLaunch.stopLaunch(); - mWaitingForReadyEmulatorList.remove(launchInfo); + if (launchInfo.getDevice() == device) { + AdtPlugin.printErrorToConsole(launchInfo.getProject(), + String.format(message, device.getSerialNumber(), + launchInfo.getLaunchAction().getLaunchDescription())); + stopLaunch(launchInfo); } } - copyList = (ArrayList<DelayedLaunchInfo>)mWaitingForDebuggerApplications.clone(); + copyList = (ArrayList<DelayedLaunchInfo>) mWaitingForDebuggerApplications.clone(); for (DelayedLaunchInfo launchInfo : copyList) { - if (launchInfo.mDevice == device) { - AdtPlugin.printErrorToConsole(launchInfo.mProject, - String.format(message, device.getSerialNumber(), launchInfo.mActivity)); - launchInfo.mLaunch.stopLaunch(); - mWaitingForDebuggerApplications.remove(launchInfo); + if (launchInfo.getDevice() == device) { + AdtPlugin.printErrorToConsole(launchInfo.getProject(), + String.format(message, device.getSerialNumber(), + launchInfo.getLaunchAction().getLaunchDescription())); + stopLaunch(launchInfo); } } } @@ -1641,13 +1300,13 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener if (home.equals(applicationName)) { // looks like home is up, get its device - Device device = client.getDevice(); + IDevice device = client.getDevice(); // look for application waiting for home synchronized (sListLock) { - for (int i = 0 ; i < mWaitingForReadyEmulatorList.size() ;) { + for (int i = 0; i < mWaitingForReadyEmulatorList.size(); ) { DelayedLaunchInfo launchInfo = mWaitingForReadyEmulatorList.get(i); - if (launchInfo.mDevice == device) { + if (launchInfo.getDevice() == device) { // it's match, remove from the list mWaitingForReadyEmulatorList.remove(i); @@ -1657,13 +1316,13 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener // so we check now if (checkBuildInfo(launchInfo, device) == false) { // device is not the proper API! - AdtPlugin.printErrorToConsole(launchInfo.mProject, + AdtPlugin.printErrorToConsole(launchInfo.getProject(), "Launch canceled!"); - launchInfo.mLaunch.stopLaunch(); + stopLaunch(launchInfo); return; } - AdtPlugin.printToConsole(launchInfo.mProject, + AdtPlugin.printToConsole(launchInfo.getProject(), String.format("HOME is up on device '%1$s'", device.getSerialNumber())); @@ -1673,9 +1332,9 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener launchApp(launchInfo, device); } else { // failure! Cancel and return - AdtPlugin.printErrorToConsole(launchInfo.mProject, + AdtPlugin.printErrorToConsole(launchInfo.getProject(), "Launch canceled!"); - launchInfo.mLaunch.stopLaunch(); + stopLaunch(launchInfo); } break; @@ -1727,18 +1386,18 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener String applicationName = client.getClientData().getClientDescription(); Log.d("adt", "App Name: " + applicationName); synchronized (sListLock) { - for (int i = 0 ; i < mWaitingForDebuggerApplications.size() ;) { + for (int i = 0; i < mWaitingForDebuggerApplications.size(); ) { final DelayedLaunchInfo launchInfo = mWaitingForDebuggerApplications.get(i); - if (client.getDevice() == launchInfo.mDevice && - applicationName.equals(launchInfo.mPackageName)) { + if (client.getDevice() == launchInfo.getDevice() && + applicationName.equals(launchInfo.getPackageName())) { // this is a match. We remove the launch info from the list mWaitingForDebuggerApplications.remove(i); // and connect the debugger. String msg = String.format( "Attempting to connect debugger to '%1$s' on port %2$d", - launchInfo.mPackageName, client.getDebuggerListenPort()); - AdtPlugin.printToConsole(launchInfo.mProject, msg); + launchInfo.getPackageName(), client.getDebuggerListenPort()); + AdtPlugin.printToConsole(launchInfo.getProject(), msg); new Thread("Debugger Connection") { //$NON-NLS-1$ @Override @@ -1746,18 +1405,19 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener try { if (connectRemoteDebugger( client.getDebuggerListenPort(), - launchInfo.mLaunch, launchInfo.mMonitor) == false) { + launchInfo.getLaunch(), + launchInfo.getMonitor()) == false) { return; } } catch (CoreException e) { // well something went wrong. - AdtPlugin.printErrorToConsole(launchInfo.mProject, + AdtPlugin.printErrorToConsole(launchInfo.getProject(), String.format("Launch error: %s", e.getMessage())); // stop the launch - launchInfo.mLaunch.stopLaunch(); + stopLaunch(launchInfo); } - launchInfo.mMonitor.done(); + launchInfo.getMonitor().done(); } }.start(); @@ -1833,4 +1493,15 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener }.start(); } + /* (non-Javadoc) + * @see com.android.ide.eclipse.adt.launch.ILaunchController#stopLaunch(com.android.ide.eclipse.adt.launch.AndroidLaunchController.DelayedLaunchInfo) + */ + public void stopLaunch(DelayedLaunchInfo launchInfo) { + launchInfo.getLaunch().stopLaunch(); + synchronized (sListLock) { + mWaitingForReadyEmulatorList.remove(launchInfo); + mWaitingForDebuggerApplications.remove(launchInfo); + } + } } + diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DelayedLaunchInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DelayedLaunchInfo.java new file mode 100644 index 0000000..a59518c --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DelayedLaunchInfo.java @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.android.ide.eclipse.adt.launch; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IProgressMonitor; + +import com.android.ddmlib.IDevice; + +/** + * A delayed launch waiting for a device to be present or ready before the + * application is launched. + */ +public final class DelayedLaunchInfo { + + /** + * Used to indicate behavior when Android app already exists + */ + enum InstallRetryMode { + NEVER, ALWAYS, PROMPT; + } + + /** The device on which to launch the app */ + private IDevice mDevice = null; + + /** The eclipse project */ + private final IProject mProject; + + /** Package name */ + private final String mPackageName; + + /** IFile to the package (.apk) file */ + private final IFile mPackageFile; + + /** debuggable attribute of the manifest file. */ + private final Boolean mDebuggable; + + /** Required ApiVersionNumber by the app. 0 means no requirements */ + private final int mRequiredApiVersionNumber; + + private InstallRetryMode mRetryMode = InstallRetryMode.NEVER; + + /** Launch action. */ + private final IAndroidLaunchAction mLaunchAction; + + /** the launch object */ + private final AndroidLaunch mLaunch; + + /** the monitor object */ + private final IProgressMonitor mMonitor; + + /** debug mode flag */ + private boolean mDebugMode; + + /** current number of launch attempts */ + private int mAttemptCount = 0; + + /** cancellation state of launch */ + private boolean mCancelled = false; + + /** + * Basic constructor with activity and package info. + * + * @param project the eclipse project that corresponds to Android app + * @param packageName package name of Android app + * @param launchAction action to perform after app install + * @param pack IFile to the package (.apk) file + * @param debuggable debuggable attribute of the app's manifest file. + * @param requiredApiVersionNumber required SDK version by the app. 0 means no requirements. + * @param launch the launch object + * @param monitor progress monitor for launch + */ + public DelayedLaunchInfo(IProject project, String packageName, + IAndroidLaunchAction launchAction, IFile pack, Boolean debuggable, + int requiredApiVersionNumber, AndroidLaunch launch, IProgressMonitor monitor) { + mProject = project; + mPackageName = packageName; + mPackageFile = pack; + mLaunchAction = launchAction; + mLaunch = launch; + mMonitor = monitor; + mDebuggable = debuggable; + mRequiredApiVersionNumber = requiredApiVersionNumber; + } + + /** + * @return the device on which to launch the app + */ + public IDevice getDevice() { + return mDevice; + } + + /** + * Set the device on which to launch the app + */ + public void setDevice(IDevice device) { + mDevice = device; + } + + /** + * @return the eclipse project that corresponds to Android app + */ + public IProject getProject() { + return mProject; + } + + /** + * @return the package name of the Android app + */ + public String getPackageName() { + return mPackageName; + } + + /** + * @return the application package file + */ + public IFile getPackageFile() { + return mPackageFile; + } + + /** + * @return true if Android app is marked as debuggable in its manifest + */ + public Boolean getDebuggable() { + return mDebuggable; + } + + /** + * @return the required api version number for the Android app + */ + public int getRequiredApiVersionNumber() { + return mRequiredApiVersionNumber; + } + + /** + * @param retryMode the install retry mode to set + */ + public void setRetryMode(InstallRetryMode retryMode) { + this.mRetryMode = retryMode; + } + + /** + * @return the installation retry mode + */ + public InstallRetryMode getRetryMode() { + return mRetryMode; + } + + /** + * @return the launch action + */ + public IAndroidLaunchAction getLaunchAction() { + return mLaunchAction; + } + + /** + * @return the launch + */ + public AndroidLaunch getLaunch() { + return mLaunch; + } + + /** + * @return the launch progress monitor + */ + public IProgressMonitor getMonitor() { + return mMonitor; + } + + /** + * @param debugMode the debug mode to set + */ + public void setDebugMode(boolean debugMode) { + this.mDebugMode = debugMode; + } + + /** + * @return true if this is a debug launch + */ + public boolean isDebugMode() { + return mDebugMode; + } + + /** + * Increases the number of launch attempts + */ + public void incrementAttemptCount() { + mAttemptCount++; + } + + /** + * @return the number of launch attempts made + */ + public int getAttemptCount() { + return mAttemptCount; + } + + /** + * Set if launch has been cancelled + */ + public void setCancelled(boolean cancelled) { + this.mCancelled = cancelled; + } + + /** + * @return true if launch has been cancelled + */ + public boolean isCancelled() { + return mCancelled; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DeviceChooserDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DeviceChooserDialog.java index a960bda..13bb83a 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DeviceChooserDialog.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DeviceChooserDialog.java @@ -231,9 +231,9 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener public static class DeviceChooserResponse { private AvdInfo mAvdToLaunch; - private Device mDeviceToUse; + private IDevice mDeviceToUse; - public void setDeviceToUse(Device d) { + public void setDeviceToUse(IDevice d) { mDeviceToUse = d; mAvdToLaunch = null; } @@ -243,7 +243,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener mDeviceToUse = null; } - public Device getDeviceToUse() { + public IDevice getDeviceToUse() { return mDeviceToUse; } @@ -737,3 +737,4 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener mDisableAvdSelectionChange = false; } } + diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmptyLaunchAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmptyLaunchAction.java new file mode 100644 index 0000000..02ae675 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmptyLaunchAction.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.android.ide.eclipse.adt.launch; + +import com.android.ddmlib.IDevice; +import com.android.ide.eclipse.adt.AdtPlugin; + +/** + * A launch action that does nothing after the application has been installed + */ +public class EmptyLaunchAction implements IAndroidLaunchAction { + + public boolean doLaunchAction(DelayedLaunchInfo info, IDevice device) { + // we're not supposed to do anything, just return; + String msg = String.format("%1$s installed on device", + info.getPackageFile().getFullPath().toOSString()); + AdtPlugin.printToConsole(info.getProject(), msg, "Done!"); + // return false so launch controller will not wait for debugger to attach + return false; + } + + public String getLaunchDescription() { + return "sync"; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/IAndroidLaunchAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/IAndroidLaunchAction.java new file mode 100644 index 0000000..2f3cb89 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/IAndroidLaunchAction.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.android.ide.eclipse.adt.launch; + +import com.android.ddmlib.IDevice; +import com.android.ide.eclipse.adt.launch.DelayedLaunchInfo; + +/** + * An action to perform after performing a launch of an Android application + */ +public interface IAndroidLaunchAction { + + /** + * Do the launch + * + * @param info the {@link DelayedLaunchInfo} that contains launch details + * @param device the Android device to perform action on + * @returns true if launch was successfully, and controller should wait for debugger to attach + * (if applicable) + */ + boolean doLaunchAction(DelayedLaunchInfo info, IDevice device); + + /** + * Return a description of launch, to be used for logging and error messages + */ + String getLaunchDescription(); + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/ILaunchController.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/ILaunchController.java new file mode 100644 index 0000000..2372c2d --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/ILaunchController.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.android.ide.eclipse.adt.launch; + +import com.android.ddmlib.IDevice; + +/** + * Interface for managing Android launches + */ +public interface ILaunchController { + + /** + * Launches an application on a device or emulator + * + * @param launchInfo the {@link DelayedLaunchInfo} that indicates the launch action + * @param device the device or emulator to launch the application on + */ + public void launchApp(DelayedLaunchInfo launchInfo, IDevice device); + + /** + * Cancels a launch + * + * @param launchInfo the {@link DelayedLaunchInfo} to cancel + */ + void stopLaunch(DelayedLaunchInfo launchInfo); +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java index a46f56c..80f62ea 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/LaunchConfigDelegate.java @@ -18,7 +18,6 @@ package com.android.ide.eclipse.adt.launch; import com.android.ddmlib.AndroidDebugBridge; import com.android.ide.eclipse.adt.AdtPlugin; -import com.android.ide.eclipse.adt.launch.AndroidLaunchController.AndroidLaunchConfiguration; import com.android.ide.eclipse.adt.project.ProjectHelper; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.project.AndroidManifestParser; @@ -233,7 +232,16 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate { return; } - String activityName = null; + doLaunch(configuration, mode, monitor, project, androidLaunch, config, controller, + applicationPackage, manifestParser); + } + + protected void doLaunch(ILaunchConfiguration configuration, String mode, + IProgressMonitor monitor, IProject project, AndroidLaunch androidLaunch, + AndroidLaunchConfiguration config, AndroidLaunchController controller, + IFile applicationPackage, AndroidManifestParser manifestParser) { + + String activityName = null; if (config.mLaunchAction == ACTION_ACTIVITY) { // Get the activity name defined in the config @@ -292,11 +300,16 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate { } } + IAndroidLaunchAction launchAction = new EmptyLaunchAction(); + if (activityName != null) { + launchAction = new ActivityLaunchAction(activityName, controller); + } + // everything seems fine, we ask the launch controller to handle // the rest controller.launch(project, mode, applicationPackage, manifestParser.getPackage(), manifestParser.getDebuggable(), manifestParser.getApiLevelRequirement(), - activityName, config, androidLaunch, monitor); + launchAction, config, androidLaunch, monitor); } @Override diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java index fd0c045..c650b98 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java @@ -400,6 +400,9 @@ public final class ProjectHelper { try { parser = AndroidManifestParser.parseForData(manifestFile); } catch (CoreException e) { + // ignore, handled below. + } + if (parser == null) { // skip this project. continue; } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java index d686830..5aeb335 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java @@ -253,8 +253,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit if (markerMessage != null) { // log the error and put the marker on the project if we can. if (outputToConsole) { - AdtPlugin.printBuildToConsole(AdtConstants.BUILD_ALWAYS, iProject, - markerMessage); + AdtPlugin.printErrorToConsole(iProject, markerMessage); } try { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java index 6c4f4ba..0dd88c0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java @@ -36,7 +36,6 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; @@ -823,12 +822,7 @@ public class NewProjectCreationPage extends WizardPage { Path path = new Path(f.getPath()); String osPath = path.append(AndroidConstants.FN_ANDROID_MANIFEST).toOSString(); - AndroidManifestParser manifestData = null; - try { - manifestData = AndroidManifestParser.parseForData(osPath); - } catch (CoreException e1) { - // ignore any parsing issue - } + AndroidManifestParser manifestData = AndroidManifestParser.parseForData(osPath); if (manifestData == null) { return; } @@ -1096,10 +1090,8 @@ public class NewProjectCreationPage extends WizardPage { } // Parse it and check the important fields. - AndroidManifestParser manifestData; - try { - manifestData = AndroidManifestParser.parseForData(osPath); - } catch (CoreException e) { + AndroidManifestParser manifestData = AndroidManifestParser.parseForData(osPath); + if (manifestData == null) { return setStatus( String.format("File %1$s could not be parsed.", osPath), MSG_ERROR); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java index b2817ff..0a45196 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java @@ -686,7 +686,8 @@ public class AndroidManifestParser { * Parses the manifest file, and collects data. * @param manifestFile The manifest file to parse. * @return an {@link AndroidManifestParser} or null if the parsing failed. - * @throws CoreException + * @throws CoreException for example the file does not exist in the workspace or + * the workspace needs to be refreshed. */ public static AndroidManifestParser parseForData(IFile manifestFile) throws CoreException { return parse(null /* javaProject */, manifestFile, null /* errorListener */, @@ -698,11 +699,15 @@ public class AndroidManifestParser { * * @param osManifestFilePath The OS path of the manifest file to parse. * @return an {@link AndroidManifestParser} or null if the parsing failed. - * @throws CoreException */ - public static AndroidManifestParser parseForData(String osManifestFilePath) - throws CoreException { - return parse(new File(osManifestFilePath)); + public static AndroidManifestParser parseForData(String osManifestFilePath) { + try { + return parse(new File(osManifestFilePath)); + } catch (CoreException e) { + // Ignore workspace errors (unlikely to happen since this parses an actual file, + // not a workspace resource). + return null; + } } /** diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/AbstractGraphicalLayoutEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/AbstractGraphicalLayoutEditor.java new file mode 100644 index 0000000..0499867 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/AbstractGraphicalLayoutEditor.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.android.ide.eclipse.editors.layout; + +import com.android.ide.eclipse.editors.layout.LayoutReloadMonitor.ILayoutReloadListener; +import com.android.ide.eclipse.editors.layout.parts.ElementCreateCommand; +import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration; +import com.android.ide.eclipse.editors.uimodel.UiDocumentNode; +import com.android.ide.eclipse.editors.uimodel.UiElementNode; + +import org.eclipse.gef.DefaultEditDomain; +import org.eclipse.gef.ui.parts.GraphicalEditorWithPalette; +import org.eclipse.gef.ui.parts.SelectionSynchronizer; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.ui.IWorkbenchPart; + +/** + * Abstract GraphicalLayoutEditor. + */ +/*package*/ abstract class AbstractGraphicalLayoutEditor extends GraphicalEditorWithPalette + implements IWorkbenchPart, ILayoutReloadListener { + + /** + * Sets the UI for the edition of a new file. + * @param configuration the configuration of the new file. + */ + abstract void editNewFile(FolderConfiguration configuration); + + /** + * Reloads this editor, by getting the new model from the {@link LayoutEditor}. + */ + abstract void reloadEditor(); + + /** + * Callback for XML model changed. Only update/recompute the layout if the editor is visible + */ + abstract void onXmlModelChanged(); + + /** + * Responds to a page change that made the Graphical editor page the activated page. + */ + abstract void activated(); + + /** + * Responds to a page change that made the Graphical editor page the deactivated page + */ + abstract void deactivated(); + + /** + * Used by LayoutEditor.UiEditorActions.selectUiNode to select a new UI Node + * created by {@link ElementCreateCommand#execute()}. + * + * @param uiNodeModel The {@link UiElementNode} to select. + */ + abstract void selectModel(UiElementNode uiNodeModel); + + /** + * Returns the selection synchronizer object. + * The synchronizer can be used to sync the selection of 2 or more EditPartViewers. + * <p/> + * This is changed from protected to public so that the outline can use it. + * + * @return the synchronizer + */ + @Override + public SelectionSynchronizer getSelectionSynchronizer() { + return super.getSelectionSynchronizer(); + } + + /** + * Returns the edit domain. + * <p/> + * This is changed from protected to public so that the outline can use it. + * + * @return the edit domain + */ + @Override + public DefaultEditDomain getEditDomain() { + return super.getEditDomain(); + } + + abstract void reloadPalette(); + + abstract void recomputeLayout(); + + abstract UiDocumentNode getModel(); + + abstract LayoutEditor getLayoutEditor(); + + abstract Clipboard getClipboard(); + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java index eb7dee6..9c529e5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java @@ -86,8 +86,6 @@ import org.eclipse.gef.dnd.TemplateTransferDropTargetListener; import org.eclipse.gef.editparts.ScalableFreeformRootEditPart; import org.eclipse.gef.palette.PaletteRoot; import org.eclipse.gef.requests.CreationFactory; -import org.eclipse.gef.ui.parts.GraphicalEditorWithPalette; -import org.eclipse.gef.ui.parts.SelectionSynchronizer; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; @@ -141,7 +139,7 @@ import java.util.Set; * <p/> * To understand Drag'n'drop: http://www.eclipse.org/articles/Article-Workbench-DND/drag_drop.html */ -public class GraphicalLayoutEditor extends GraphicalEditorWithPalette +public class GraphicalLayoutEditor extends AbstractGraphicalLayoutEditor implements ILayoutReloadListener { private final static String THEME_SEPARATOR = "----------"; //$NON-NLS-1$ @@ -595,6 +593,7 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette return mPaletteRoot; } + @Override public Clipboard getClipboard() { return mClipboard; } @@ -716,7 +715,8 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette * * @param uiNodeModel The {@link UiElementNode} to select. */ - public void selectModel(UiElementNode uiNodeModel) { + @Override + void selectModel(UiElementNode uiNodeModel) { GraphicalViewer viewer = getGraphicalViewer(); // Give focus to the graphical viewer (in case the outline has it) @@ -734,6 +734,7 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette // Local methods //-------------- + @Override public LayoutEditor getLayoutEditor() { return mLayoutEditor; } @@ -863,7 +864,8 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette * Sets the UI for the edition of a new file. * @param configuration the configuration of the new file. */ - public void editNewFile(FolderConfiguration configuration) { + @Override + void editNewFile(FolderConfiguration configuration) { // update the configuration UI setConfiguration(configuration); @@ -1015,6 +1017,7 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette /** * Reloads this editor, by getting the new model from the {@link LayoutEditor}. */ + @Override void reloadEditor() { GraphicalViewer viewer = getGraphicalViewer(); viewer.setContents(getModel()); @@ -1036,6 +1039,7 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette /** * Callback for XML model changed. Only update/recompute the layout if the editor is visible */ + @Override void onXmlModelChanged() { if (mLayoutEditor.isGraphicalEditorActive()) { doXmlReload(true /* force */); @@ -1265,10 +1269,12 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette mCurrentLayoutLabel.setText(current != null ? current : "(Default)"); } + @Override UiDocumentNode getModel() { return mLayoutEditor.getUiRootNode(); } + @Override void reloadPalette() { PaletteFactory.createPaletteRoot(mPaletteRoot, mLayoutEditor.getTargetData()); } @@ -1667,6 +1673,7 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette /** * Recomputes the layout with the help of layoutlib. */ + @Override @SuppressWarnings("deprecation") void recomputeLayout() { doXmlReload(false /* force */); @@ -1968,6 +1975,7 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette /** * Responds to a page change that made the Graphical editor page the activated page. */ + @Override void activated() { if (mNeedsRecompute || mNeedsXmlReload) { recomputeLayout(); @@ -1977,6 +1985,7 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette /** * Responds to a page change that made the Graphical editor page the deactivated page */ + @Override void deactivated() { // nothing to be done here for now. } @@ -2234,31 +2243,6 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette } /** - * Returns the selection synchronizer object. - * The synchronizer can be used to sync the selection of 2 or more EditPartViewers. - * <p/> - * This is changed from protected to public so that the outline can use it. - * - * @return the synchronizer - */ - @Override - public SelectionSynchronizer getSelectionSynchronizer() { - return super.getSelectionSynchronizer(); - } - - /** - * Returns the edit domain. - * <p/> - * This is changed from protected to public so that the outline can use it. - * - * @return the edit domain - */ - @Override - public DefaultEditDomain getEditDomain() { - return super.getEditDomain(); - } - - /** * Creates a new layout file from the specificed {@link FolderConfiguration}. */ private void createAlternateLayout(final FolderConfiguration config) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java index dabe797..f3a5113 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java @@ -55,7 +55,7 @@ public class LayoutEditor extends AndroidEditor implements IShowEditorInput, IPa /** Root node of the UI element hierarchy */ private UiDocumentNode mUiRootNode; - private GraphicalLayoutEditor mGraphicalEditor; + private AbstractGraphicalLayoutEditor mGraphicalEditor; private int mGraphicalEditorIndex; /** Implementation of the {@link IContentOutlinePage} for this editor */ private UiContentOutlinePage mOutline; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java index 3e0f5d8..536e902 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/UiContentOutlinePage.java @@ -70,7 +70,7 @@ import java.util.List; */ class UiContentOutlinePage extends ContentOutlinePage { - private GraphicalLayoutEditor mEditor; + private AbstractGraphicalLayoutEditor mEditor; private Action mAddAction; private Action mDeleteAction; @@ -79,7 +79,7 @@ class UiContentOutlinePage extends ContentOutlinePage { private UiOutlineActions mUiActions = new UiOutlineActions(); - public UiContentOutlinePage(GraphicalLayoutEditor editor, final EditPartViewer viewer) { + public UiContentOutlinePage(AbstractGraphicalLayoutEditor editor, final EditPartViewer viewer) { super(viewer); mEditor = editor; IconFactory factory = IconFactory.getInstance(); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java index c3f4dd8..5726d78 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java @@ -18,7 +18,6 @@ package com.android.ide.eclipse.editors.layout.descriptors; import com.android.ide.eclipse.common.AndroidConstants; import com.android.ide.eclipse.common.resources.DeclareStyleableInfo; -import com.android.ide.eclipse.common.resources.ResourceType; import com.android.ide.eclipse.common.resources.ViewClassInfo; import com.android.ide.eclipse.common.resources.DeclareStyleableInfo.AttributeInfo; import com.android.ide.eclipse.common.resources.ViewClassInfo.LayoutParamsInfo; @@ -27,7 +26,6 @@ import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils; import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.editors.descriptors.IDescriptorProvider; -import com.android.ide.eclipse.editors.descriptors.ReferenceAttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.SeparatorAttributeDescriptor; import com.android.sdklib.SdkConstants; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/CompiledResourcesMonitor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/CompiledResourcesMonitor.java index fa09305..2d14c06 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/CompiledResourcesMonitor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/CompiledResourcesMonitor.java @@ -217,8 +217,10 @@ public final class CompiledResourcesMonitor implements IFileListener, IProjectLi try { IFile manifestFile = AndroidManifestParser.getManifest(project); AndroidManifestParser data = AndroidManifestParser.parseForData(manifestFile); - String javaPackage = data.getPackage(); - return javaPackage + ".R"; //$NON-NLS-1$ + if (data != null) { + String javaPackage = data.getPackage(); + return javaPackage + ".R"; //$NON-NLS-1$ + } } catch (CoreException e) { // This will typically happen either because the manifest file is not present // and/or the workspace needs to be refreshed. @@ -227,8 +229,8 @@ public final class CompiledResourcesMonitor implements IFileListener, IProjectLi "Failed to find the package of the AndroidManifest of project %1$s. Reason: %2$s", project.getName(), e.getMessage()); - return null; } + return null; } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java index 48f8a7f..32cac9f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/uimodel/UiResourceAttributeNode.java @@ -158,6 +158,35 @@ public class UiResourceAttributeNode extends UiTextAttributeNode { return null; } + /** + * Gets all the values one could use to auto-complete a "resource" value in an XML + * content assist. + * <p/> + * Typically the user is editing the value of an attribute in a resource XML, e.g. + * <pre> "<Button android:test="@string/my_[caret]_string..." </pre> + * <p/> + * + * "prefix" is the value that the user has typed so far (or more exactly whatever is on the + * left side of the insertion point). In the example above it would be "@style/my_". + * <p/> + * + * To avoid a huge long list of values, the completion works on two levels: + * <ul> + * <li> If a resource type as been typed so far (e.g. "@style/"), then limit the values to + * the possible completions that match this type. + * <li> If no resource type as been typed so far, then return the various types that could be + * completed. So if the project has only strings and layouts resources, for example, + * the returned list will only include "@string/" and "@layout/". + * </ul> + * + * Finally if anywhere in the string we find the special token "android:", we use the + * current framework system resources rather than the project resources. + * This works for both "@android:style/foo" and "@style/android:foo" conventions even though + * the reconstructed name will always be of the former form. + * + * Note that "android:" here is a keyword specific to Android resources and should not be + * mixed with an XML namespace for an XML attribute name. + */ @Override public String[] getPossibleValues(String prefix) { IResourceRepository repository = null; @@ -174,6 +203,8 @@ public class UiResourceAttributeNode extends UiTextAttributeNode { } } else { // If there's a prefix with "android:" in it, use the system resources + // + // TODO find a way to only list *public* framework resources here. AndroidTargetData data = editor.getTargetData(); repository = data.getSystemResources(); isSystem = true; |