auto import from //branches/cupcake/...@131421
This commit is contained in:
@@ -166,8 +166,12 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
/** Load status of the SDK. Any access MUST be in a synchronized(mPostLoadProjects) block */
|
||||
private LoadStatus mSdkIsLoaded = LoadStatus.LOADING;
|
||||
/** Project to update once the SDK is loaded.
|
||||
* Any access MUST be in a synchronized(mPostLoadProjects) block */
|
||||
private final ArrayList<IJavaProject> mPostLoadProjects = new ArrayList<IJavaProject>();
|
||||
* Any access MUST be in a synchronized(mPostLoadProjectsToResolve) block */
|
||||
private final ArrayList<IJavaProject> mPostLoadProjectsToResolve =
|
||||
new ArrayList<IJavaProject>();
|
||||
/** Project to check validity of cache vs actual once the SDK is loaded.
|
||||
* Any access MUST be in a synchronized(mPostLoadProjectsToResolve) block */
|
||||
private final ArrayList<IJavaProject> mPostLoadProjectsToCheck = new ArrayList<IJavaProject>();
|
||||
|
||||
private ResourceMonitor mResourceMonitor;
|
||||
private ArrayList<Runnable> mResourceRefreshListener = new ArrayList<Runnable>();
|
||||
@@ -306,12 +310,12 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
if (checkSdkLocationAndId()) {
|
||||
// if sdk if valid, reparse it
|
||||
|
||||
// add the current Android project to the list of projects to be updated
|
||||
// add all the opened Android projects to the list of projects to be updated
|
||||
// after the SDK is reloaded
|
||||
synchronized (mPostLoadProjects) {
|
||||
synchronized (getSdkLockObject()) {
|
||||
// get the project to refresh.
|
||||
IJavaProject[] androidProjects = BaseProjectHelper.getAndroidProjects();
|
||||
mPostLoadProjects.addAll(Arrays.asList(androidProjects));
|
||||
mPostLoadProjectsToResolve.addAll(Arrays.asList(androidProjects));
|
||||
}
|
||||
|
||||
// parse the SDK resources at the new location
|
||||
@@ -869,19 +873,44 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the Sdk has been loaded. If the SDK has not been loaded, the given
|
||||
* <var>project</var> is added to a list of projects to recompile after the SDK is loaded.
|
||||
* Returns whether the Sdk has been loaded.
|
||||
*/
|
||||
public LoadStatus getSdkLoadStatus(IJavaProject project) {
|
||||
synchronized (mPostLoadProjects) {
|
||||
// only add the project to the list, if we are still loading.
|
||||
if (mSdkIsLoaded == LoadStatus.LOADING && project != null) {
|
||||
mPostLoadProjects.add(project);
|
||||
}
|
||||
|
||||
public final LoadStatus getSdkLoadStatus() {
|
||||
synchronized (getSdkLockObject()) {
|
||||
return mSdkIsLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lock object for SDK loading. If you wish to do things while the SDK is loading,
|
||||
* you must synchronize on this object.
|
||||
* @return
|
||||
*/
|
||||
public final Object getSdkLockObject() {
|
||||
return mPostLoadProjectsToResolve;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given {@link IJavaProject} to have its target resolved again once the SDK finishes
|
||||
* to load.
|
||||
*/
|
||||
public final void setProjectToResolve(IJavaProject javaProject) {
|
||||
synchronized (getSdkLockObject()) {
|
||||
mPostLoadProjectsToResolve.add(javaProject);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given {@link IJavaProject} to have its target checked for consistency
|
||||
* once the SDK finishes to load. This is used if the target is resolved using cached
|
||||
* information while the SDK is loading.
|
||||
*/
|
||||
public final void setProjectToCheck(IJavaProject javaProject) {
|
||||
// only lock on
|
||||
synchronized (getSdkLockObject()) {
|
||||
mPostLoadProjectsToCheck.add(javaProject);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the location of the SDK is valid and if it is, grab the SDK API version
|
||||
@@ -1018,9 +1047,9 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
for (IAndroidTarget target : sdk.getTargets()) {
|
||||
IStatus status = new AndroidTargetParser(target).run(progress);
|
||||
if (status.getCode() != IStatus.OK) {
|
||||
synchronized (mPostLoadProjects) {
|
||||
synchronized (getSdkLockObject()) {
|
||||
mSdkIsLoaded = LoadStatus.FAILED;
|
||||
mPostLoadProjects.clear();
|
||||
mPostLoadProjectsToResolve.clear();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -1034,22 +1063,30 @@ public class AdtPlugin extends AbstractUIPlugin {
|
||||
IStatus res = DexWrapper.loadDex(
|
||||
mOsSdkLocation + AndroidConstants.OS_SDK_LIBS_DX_JAR);
|
||||
if (res != Status.OK_STATUS) {
|
||||
synchronized (mPostLoadProjects) {
|
||||
synchronized (getSdkLockObject()) {
|
||||
mSdkIsLoaded = LoadStatus.FAILED;
|
||||
mPostLoadProjects.clear();
|
||||
mPostLoadProjectsToResolve.clear();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
synchronized (mPostLoadProjects) {
|
||||
synchronized (getSdkLockObject()) {
|
||||
mSdkIsLoaded = LoadStatus.LOADED;
|
||||
|
||||
// check the projects that need checking.
|
||||
// The method modifies the list (it removes the project that
|
||||
// do not need to be resolved again).
|
||||
AndroidClasspathContainerInitializer.checkProjectsCache(
|
||||
mPostLoadProjectsToCheck);
|
||||
|
||||
mPostLoadProjectsToResolve.addAll(mPostLoadProjectsToCheck);
|
||||
|
||||
// update the project that needs recompiling.
|
||||
if (mPostLoadProjects.size() > 0) {
|
||||
IJavaProject[] array = mPostLoadProjects.toArray(
|
||||
new IJavaProject[mPostLoadProjects.size()]);
|
||||
if (mPostLoadProjectsToResolve.size() > 0) {
|
||||
IJavaProject[] array = mPostLoadProjectsToResolve.toArray(
|
||||
new IJavaProject[mPostLoadProjectsToResolve.size()]);
|
||||
AndroidClasspathContainerInitializer.updateProjects(array);
|
||||
mPostLoadProjects.clear();
|
||||
mPostLoadProjectsToResolve.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ public class ApkBuilder extends BaseBuilder {
|
||||
|
||||
// At this point, we can abort the build if we have to, as we have computed
|
||||
// our resource delta and stored the result.
|
||||
abortOnBadSetup(javaProject);
|
||||
abortOnBadSetup(project);
|
||||
|
||||
if (dv != null && dv.mXmlError) {
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
|
||||
|
||||
@@ -854,15 +854,15 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
|
||||
* @param javaProject The {@link IJavaProject} being compiled.
|
||||
* @throws CoreException
|
||||
*/
|
||||
protected final void abortOnBadSetup(IJavaProject javaProject) throws CoreException {
|
||||
protected final void abortOnBadSetup(IProject project) throws CoreException {
|
||||
// check if we have finished loading the SDK.
|
||||
if (AdtPlugin.getDefault().getSdkLoadStatus(javaProject) != LoadStatus.LOADED) {
|
||||
if (AdtPlugin.getDefault().getSdkLoadStatus() != LoadStatus.LOADED) {
|
||||
// we exit silently
|
||||
stopBuild("SDK is not loaded yet");
|
||||
}
|
||||
|
||||
// check the compiler compliance level.
|
||||
if (ProjectHelper.checkCompilerCompliance(getProject()) !=
|
||||
if (ProjectHelper.checkCompilerCompliance(project) !=
|
||||
ProjectHelper.COMPILER_COMPLIANCE_OK) {
|
||||
// we exit silently
|
||||
stopBuild(Messages.Compiler_Compliance_Error);
|
||||
@@ -875,7 +875,7 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
|
||||
stopBuild(Messages.No_SDK_Setup_Error);
|
||||
}
|
||||
|
||||
IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(javaProject.getProject());
|
||||
IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project);
|
||||
if (projectTarget == null) {
|
||||
// no target. error has been output by the container initializer:
|
||||
// exit silently.
|
||||
|
||||
@@ -285,7 +285,7 @@ public class PreCompilerBuilder extends BaseBuilder {
|
||||
|
||||
// At this point we have stored what needs to be build, so we can
|
||||
// do some high level test and abort if needed.
|
||||
abortOnBadSetup(javaProject);
|
||||
abortOnBadSetup(project);
|
||||
|
||||
// if there was some XML errors, we just return w/o doing
|
||||
// anything since we've put some markers in the files anyway.
|
||||
|
||||
@@ -31,8 +31,6 @@ import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -95,8 +93,7 @@ public class ResourceManagerBuilder extends BaseBuilder {
|
||||
}
|
||||
|
||||
// check if we have finished loading the SDK.
|
||||
IJavaProject javaProject = JavaCore.create(project);
|
||||
if (AdtPlugin.getDefault().getSdkLoadStatus(javaProject) != LoadStatus.LOADED) {
|
||||
if (AdtPlugin.getDefault().getSdkLoadStatus() != LoadStatus.LOADED) {
|
||||
// we exit silently
|
||||
// This interrupts the build. The next builders will not run.
|
||||
stopBuild("SDK is not loaded yet");
|
||||
|
||||
@@ -54,6 +54,7 @@ import org.eclipse.debug.ui.DebugUITools;
|
||||
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
|
||||
import org.eclipse.jdt.launching.IVMConnector;
|
||||
import org.eclipse.jdt.launching.JavaRuntime;
|
||||
import org.eclipse.jface.dialogs.Dialog;
|
||||
import org.eclipse.jface.dialogs.MessageDialog;
|
||||
import org.eclipse.jface.preference.IPreferenceStore;
|
||||
|
||||
@@ -133,7 +134,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
boolean mCancelled = false;
|
||||
|
||||
/** Basic constructor with activity and package info. */
|
||||
public DelayedLaunchInfo(IProject project, String packageName, String activity,
|
||||
private DelayedLaunchInfo(IProject project, String packageName, String activity,
|
||||
IFile pack, Boolean debuggable, int requiredApiVersionNumber, int launchAction,
|
||||
AndroidLaunch launch, IProgressMonitor monitor) {
|
||||
mProject = project;
|
||||
@@ -195,7 +196,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a launch configuration.
|
||||
* Launch configuration data. This stores the result of querying the
|
||||
* {@link ILaunchConfiguration} so that it's only done once.
|
||||
*/
|
||||
static final class AndroidLaunchConfiguration {
|
||||
|
||||
@@ -680,9 +682,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
for (Device d : devices) {
|
||||
String deviceAvd = d.getAvdName();
|
||||
if (deviceAvd != null && deviceAvd.equals(config.mAvdName)) {
|
||||
response.mustContinue = true;
|
||||
response.mustLaunchEmulator = false;
|
||||
response.deviceToUse = d;
|
||||
response.setDeviceToUse(d);
|
||||
|
||||
AdtPlugin.printToConsole(project, String.format(
|
||||
"Automatic Target Mode: Preferred AVD '%1$s' is available on emulator '%2$s'",
|
||||
@@ -695,9 +695,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
|
||||
// at this point we have a valid preferred AVD that is not running.
|
||||
// We need to start it.
|
||||
response.mustContinue = true;
|
||||
response.mustLaunchEmulator = true;
|
||||
response.avdToLaunch = preferredAvd;
|
||||
response.setAvdToLaunch(preferredAvd);
|
||||
|
||||
AdtPlugin.printToConsole(project, String.format(
|
||||
"Automatic Target Mode: Preferred AVD '%1$s' is not available. Launching new emulator.",
|
||||
@@ -760,9 +758,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
}
|
||||
|
||||
if (defaultAvd != null) {
|
||||
response.mustContinue = true;
|
||||
response.mustLaunchEmulator = true;
|
||||
response.avdToLaunch = defaultAvd;
|
||||
response.setAvdToLaunch(defaultAvd);
|
||||
|
||||
AdtPlugin.printToConsole(project, String.format(
|
||||
"Automatic Target Mode: launching new emulator with compatible AVD '%1$s'",
|
||||
@@ -781,18 +777,16 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
}
|
||||
} else if (hasDevice == false && compatibleRunningAvds.size() == 1) {
|
||||
Entry<Device, AvdInfo> e = compatibleRunningAvds.entrySet().iterator().next();
|
||||
response.mustContinue = true;
|
||||
response.mustLaunchEmulator = false;
|
||||
response.deviceToUse = e.getKey();
|
||||
response.setDeviceToUse(e.getKey());
|
||||
|
||||
// get the AvdInfo, if null, the device is a physical device.
|
||||
AvdInfo avdInfo = e.getValue();
|
||||
if (avdInfo != null) {
|
||||
message = String.format("Automatic Target Mode: using existing emulator '%1$s' running compatible AVD '%2$s'",
|
||||
response.deviceToUse, e.getValue().getName());
|
||||
response.getDeviceToUse(), e.getValue().getName());
|
||||
} else {
|
||||
message = String.format("Automatic Target Mode: using device '%1$s'",
|
||||
response.deviceToUse);
|
||||
response.getDeviceToUse());
|
||||
}
|
||||
AdtPlugin.printToConsole(project, message);
|
||||
|
||||
@@ -813,13 +807,35 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
// bring up the device chooser.
|
||||
AdtPlugin.getDisplay().asyncExec(new Runnable() {
|
||||
public void run() {
|
||||
DeviceChooserDialog dialog = new DeviceChooserDialog(
|
||||
AdtPlugin.getDisplay().getActiveShell());
|
||||
dialog.open(response, project, projectTarget, launch, launchInfo, config);
|
||||
try {
|
||||
// open the chooser dialog. It'll fill 'response' with the device to use
|
||||
// or the AVD to launch.
|
||||
DeviceChooserDialog dialog = new DeviceChooserDialog(
|
||||
AdtPlugin.getDisplay().getActiveShell(),
|
||||
response, launchInfo.mPackageName, projectTarget);
|
||||
if (dialog.open() == Dialog.OK) {
|
||||
AndroidLaunchController.this.continueLaunch(response, project, launch,
|
||||
launchInfo, config);
|
||||
} else {
|
||||
AdtPlugin.printErrorToConsole(project, "Launch canceled!");
|
||||
launch.stopLaunch();
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// there seems to be some case where the shell will be null. (might be
|
||||
// an OS X bug). Because of this the creation of the dialog will throw
|
||||
// and IllegalArg exception interrupting the launch with no user feedback.
|
||||
// So we trap all the exception and display something.
|
||||
String msg = e.getMessage();
|
||||
if (msg == null) {
|
||||
msg = e.getClass().getCanonicalName();
|
||||
}
|
||||
AdtPlugin.printErrorToConsole(project,
|
||||
String.format("Error during launch: %s", msg));
|
||||
launch.stopLaunch();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -833,23 +849,21 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
void continueLaunch(final DeviceChooserResponse response, final IProject project,
|
||||
final AndroidLaunch launch, final DelayedLaunchInfo launchInfo,
|
||||
final AndroidLaunchConfiguration config) {
|
||||
if (response.mustContinue == false) {
|
||||
AdtPlugin.printErrorToConsole(project, "Launch canceled!");
|
||||
launch.stopLaunch();
|
||||
return;
|
||||
}
|
||||
|
||||
// Since this is called from the DeviceChooserDialog open, we are in the UI
|
||||
// thread. So we spawn a temporary new one to finish the launch.
|
||||
// Since this is called from the UI thread we spawn a new thread
|
||||
// to finish the launch.
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (response.mustLaunchEmulator) {
|
||||
if (response.getAvdToLaunch() != null) {
|
||||
// there was no selected device, we start a new emulator.
|
||||
synchronized (sListLock) {
|
||||
AvdInfo info = response.getAvdToLaunch();
|
||||
mWaitingForEmulatorLaunches.add(launchInfo);
|
||||
AdtPlugin.printToConsole(project, "Launching a new emulator.");
|
||||
boolean status = launchEmulator(config, response.avdToLaunch);
|
||||
AdtPlugin.printToConsole(project, String.format(
|
||||
"Launching a new emulator with Virtual Device '%1$s'",
|
||||
info.getName()));
|
||||
boolean status = launchEmulator(config, info);
|
||||
|
||||
if (status == false) {
|
||||
// launching the emulator failed!
|
||||
@@ -865,9 +879,9 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
|
||||
|
||||
return;
|
||||
}
|
||||
} else if (response.deviceToUse != null) {
|
||||
launchInfo.mDevice = response.deviceToUse;
|
||||
simpleLaunch(launchInfo, response.deviceToUse);
|
||||
} else if (response.getDeviceToUse() != null) {
|
||||
launchInfo.mDevice = response.getDeviceToUse();
|
||||
simpleLaunch(launchInfo, launchInfo.mDevice);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
|
||||
@@ -26,14 +26,15 @@ import com.android.ddmuilib.IImageLoader;
|
||||
import com.android.ddmuilib.ImageHelper;
|
||||
import com.android.ddmuilib.TableHelper;
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController.AndroidLaunchConfiguration;
|
||||
import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController.DelayedLaunchInfo;
|
||||
import com.android.ide.eclipse.adt.sdk.Sdk;
|
||||
import com.android.ide.eclipse.ddms.DdmsPlugin;
|
||||
import com.android.sdklib.IAndroidTarget;
|
||||
import com.android.sdklib.avd.AvdManager;
|
||||
import com.android.sdklib.avd.AvdManager.AvdInfo;
|
||||
import com.android.sdkuilib.AvdSelector;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.jface.dialogs.Dialog;
|
||||
import org.eclipse.jface.dialogs.IDialogConstants;
|
||||
import org.eclipse.jface.preference.IPreferenceStore;
|
||||
import org.eclipse.jface.viewers.DoubleClickEvent;
|
||||
import org.eclipse.jface.viewers.IDoubleClickListener;
|
||||
@@ -50,23 +51,19 @@ import org.eclipse.swt.SWTException;
|
||||
import org.eclipse.swt.events.SelectionAdapter;
|
||||
import org.eclipse.swt.events.SelectionEvent;
|
||||
import org.eclipse.swt.graphics.Image;
|
||||
import org.eclipse.swt.graphics.Rectangle;
|
||||
import org.eclipse.swt.layout.GridData;
|
||||
import org.eclipse.swt.layout.GridLayout;
|
||||
import org.eclipse.swt.widgets.Button;
|
||||
import org.eclipse.swt.widgets.Composite;
|
||||
import org.eclipse.swt.widgets.Dialog;
|
||||
import org.eclipse.swt.widgets.Control;
|
||||
import org.eclipse.swt.widgets.Display;
|
||||
import org.eclipse.swt.widgets.Event;
|
||||
import org.eclipse.swt.widgets.Label;
|
||||
import org.eclipse.swt.widgets.Listener;
|
||||
import org.eclipse.swt.widgets.Shell;
|
||||
import org.eclipse.swt.widgets.Table;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener {
|
||||
|
||||
private final static int DLG_WIDTH = 500;
|
||||
private final static int DLG_HEIGHT = 300;
|
||||
private final static int ICON_WIDTH = 16;
|
||||
|
||||
private final static String PREFS_COL_SERIAL = "deviceChooser.serial"; //$NON-NLS-1$
|
||||
@@ -77,6 +74,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
|
||||
|
||||
private Table mDeviceTable;
|
||||
private TableViewer mViewer;
|
||||
private AvdSelector mPreferredAvdSelector;
|
||||
|
||||
private Image mDeviceImage;
|
||||
private Image mEmulatorImage;
|
||||
@@ -84,13 +82,16 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
|
||||
private Image mNoMatchImage;
|
||||
private Image mWarningImage;
|
||||
|
||||
private Button mOkButton;
|
||||
private Button mCreateButton;
|
||||
|
||||
private DeviceChooserResponse mResponse;
|
||||
private DelayedLaunchInfo mLaunchInfo;
|
||||
private IAndroidTarget mProjectTarget;
|
||||
private Sdk mSdk;
|
||||
private final DeviceChooserResponse mResponse;
|
||||
private final String mPackageName;
|
||||
private final IAndroidTarget mProjectTarget;
|
||||
private final Sdk mSdk;
|
||||
|
||||
private final AvdInfo[] mFullAvdList;
|
||||
|
||||
private Button mDeviceRadioButton;
|
||||
|
||||
private boolean mDisableAvdSelectionChange = false;
|
||||
|
||||
/**
|
||||
* Basic Content Provider for a table full of {@link Device} objects. The input is
|
||||
@@ -135,14 +136,18 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
|
||||
try {
|
||||
String apiValue = device.getProperty(
|
||||
IDevice.PROP_BUILD_VERSION_NUMBER);
|
||||
int api = Integer.parseInt(apiValue);
|
||||
if (api >= mProjectTarget.getApiVersionNumber()) {
|
||||
// if the project is compiling against an add-on, the optional
|
||||
// API may be missing from the device.
|
||||
return mProjectTarget.isPlatform() ?
|
||||
mMatchImage : mWarningImage;
|
||||
if (apiValue != null) {
|
||||
int api = Integer.parseInt(apiValue);
|
||||
if (api >= mProjectTarget.getApiVersionNumber()) {
|
||||
// if the project is compiling against an add-on, the optional
|
||||
// API may be missing from the device.
|
||||
return mProjectTarget.isPlatform() ?
|
||||
mMatchImage : mWarningImage;
|
||||
} else {
|
||||
return mNoMatchImage;
|
||||
}
|
||||
} else {
|
||||
return mNoMatchImage;
|
||||
return mWarningImage;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// lets consider the device non compatible
|
||||
@@ -183,7 +188,11 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
|
||||
}
|
||||
return info.getTarget().getFullName();
|
||||
} else {
|
||||
return device.getProperty(IDevice.PROP_BUILD_VERSION);
|
||||
String deviceBuild = device.getProperty(IDevice.PROP_BUILD_VERSION);
|
||||
if (deviceBuild == null) {
|
||||
return "unknown";
|
||||
}
|
||||
return deviceBuild;
|
||||
}
|
||||
case 3:
|
||||
String debuggable = device.getProperty(IDevice.PROP_DEBUGGABLE);
|
||||
@@ -219,62 +228,48 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
|
||||
}
|
||||
|
||||
public static class DeviceChooserResponse {
|
||||
public boolean mustContinue;
|
||||
public boolean mustLaunchEmulator;
|
||||
public AvdInfo avdToLaunch;
|
||||
public Device deviceToUse;
|
||||
}
|
||||
|
||||
public DeviceChooserDialog(Shell parent) {
|
||||
super(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare and display the dialog.
|
||||
* @param response
|
||||
* @param project
|
||||
* @param projectTarget
|
||||
* @param launch
|
||||
* @param launchInfo
|
||||
* @param config
|
||||
*/
|
||||
public void open(DeviceChooserResponse response, IProject project,
|
||||
IAndroidTarget projectTarget, AndroidLaunch launch, DelayedLaunchInfo launchInfo,
|
||||
AndroidLaunchConfiguration config) {
|
||||
mResponse = response;
|
||||
mProjectTarget = projectTarget;
|
||||
mLaunchInfo = launchInfo;
|
||||
mSdk = Sdk.getCurrent();
|
||||
|
||||
Shell parent = getParent();
|
||||
Shell shell = new Shell(parent, getStyle());
|
||||
shell.setText("Device Chooser");
|
||||
|
||||
loadImages();
|
||||
createContents(shell);
|
||||
private AvdInfo mAvdToLaunch;
|
||||
private Device mDeviceToUse;
|
||||
|
||||
// Set the dialog size.
|
||||
shell.setMinimumSize(DLG_WIDTH, DLG_HEIGHT);
|
||||
Rectangle r = parent.getBounds();
|
||||
// get the center new top left.
|
||||
int cx = r.x + r.width/2;
|
||||
int x = cx - DLG_WIDTH / 2;
|
||||
int cy = r.y + r.height/2;
|
||||
int y = cy - DLG_HEIGHT / 2;
|
||||
shell.setBounds(x, y, DLG_WIDTH, DLG_HEIGHT);
|
||||
|
||||
shell.pack();
|
||||
shell.open();
|
||||
|
||||
// start the listening.
|
||||
AndroidDebugBridge.addDeviceChangeListener(this);
|
||||
|
||||
Display display = parent.getDisplay();
|
||||
while (!shell.isDisposed()) {
|
||||
if (!display.readAndDispatch())
|
||||
display.sleep();
|
||||
public void setDeviceToUse(Device d) {
|
||||
mDeviceToUse = d;
|
||||
mAvdToLaunch = null;
|
||||
}
|
||||
|
||||
public void setAvdToLaunch(AvdInfo avd) {
|
||||
mAvdToLaunch = avd;
|
||||
mDeviceToUse = null;
|
||||
}
|
||||
|
||||
public Device getDeviceToUse() {
|
||||
return mDeviceToUse;
|
||||
}
|
||||
|
||||
public AvdInfo getAvdToLaunch() {
|
||||
return mAvdToLaunch;
|
||||
}
|
||||
}
|
||||
|
||||
public DeviceChooserDialog(Shell parent, DeviceChooserResponse response, String packageName,
|
||||
IAndroidTarget projectTarget) {
|
||||
super(parent);
|
||||
mResponse = response;
|
||||
mPackageName = packageName;
|
||||
mProjectTarget = projectTarget;
|
||||
mSdk = Sdk.getCurrent();
|
||||
|
||||
// get the full list of Android Virtual Devices
|
||||
AvdManager avdManager = mSdk.getAvdManager();
|
||||
if (avdManager != null) {
|
||||
mFullAvdList = avdManager.getAvds();
|
||||
} else {
|
||||
mFullAvdList = null;
|
||||
}
|
||||
|
||||
loadImages();
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
// done listening.
|
||||
AndroidDebugBridge.removeDeviceChangeListener(this);
|
||||
|
||||
@@ -283,30 +278,73 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
|
||||
mMatchImage.dispose();
|
||||
mNoMatchImage.dispose();
|
||||
mWarningImage.dispose();
|
||||
|
||||
AndroidLaunchController.getInstance().continueLaunch(response, project, launch,
|
||||
launchInfo, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the device chooser dialog contents.
|
||||
* @param shell the parent shell.
|
||||
*/
|
||||
private void createContents(final Shell shell) {
|
||||
shell.setLayout(new GridLayout(1, true));
|
||||
@Override
|
||||
protected void okPressed() {
|
||||
cleanup();
|
||||
super.okPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancelPressed() {
|
||||
cleanup();
|
||||
super.cancelPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Control createContents(Composite parent) {
|
||||
Control content = super.createContents(parent);
|
||||
|
||||
// this must be called after createContents() has happened so that the
|
||||
// ok button has been created (it's created after the call to createDialogArea)
|
||||
updateDefaultSelection();
|
||||
|
||||
shell.addListener(SWT.Close, new Listener() {
|
||||
public void handleEvent(Event event) {
|
||||
event.doit = true;
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Control createDialogArea(Composite parent) {
|
||||
Composite top = new Composite(parent, SWT.NONE);
|
||||
top.setLayout(new GridLayout(1, true));
|
||||
|
||||
mDeviceRadioButton = new Button(top, SWT.RADIO);
|
||||
mDeviceRadioButton.setText("Choose an Android running device");
|
||||
mDeviceRadioButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
boolean deviceMode = mDeviceRadioButton.getSelection();
|
||||
|
||||
mDeviceTable.setEnabled(deviceMode);
|
||||
mPreferredAvdSelector.setEnabled(!deviceMode);
|
||||
|
||||
if (deviceMode) {
|
||||
handleDeviceSelection();
|
||||
} else {
|
||||
mResponse.setAvdToLaunch(mPreferredAvdSelector.getFirstSelected());
|
||||
}
|
||||
|
||||
enableOkButton();
|
||||
}
|
||||
});
|
||||
mDeviceRadioButton.setSelection(true);
|
||||
|
||||
Label l = new Label(shell, SWT.NONE);
|
||||
l.setText("Select the target device.");
|
||||
|
||||
// offset the selector from the radio button
|
||||
Composite offsetComp = new Composite(top, SWT.NONE);
|
||||
offsetComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
GridLayout layout = new GridLayout(1, false);
|
||||
layout.marginRight = layout.marginHeight = 0;
|
||||
layout.marginLeft = 30;
|
||||
offsetComp.setLayout(layout);
|
||||
|
||||
IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
|
||||
mDeviceTable = new Table(shell, SWT.SINGLE | SWT.FULL_SELECTION);
|
||||
mDeviceTable.setLayoutData(new GridData(GridData.FILL_BOTH));
|
||||
mDeviceTable = new Table(offsetComp, SWT.SINGLE | SWT.FULL_SELECTION);
|
||||
GridData gd;
|
||||
mDeviceTable.setLayoutData(gd = new GridData(GridData.FILL_BOTH));
|
||||
gd.heightHint = 100;
|
||||
|
||||
mDeviceTable.setHeaderVisible(true);
|
||||
mDeviceTable.setLinesVisible(true);
|
||||
|
||||
@@ -342,91 +380,47 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
|
||||
IStructuredSelection structuredSelection = (IStructuredSelection)selection;
|
||||
Object object = structuredSelection.getFirstElement();
|
||||
if (object instanceof Device) {
|
||||
Device selectedDevice = (Device)object;
|
||||
|
||||
mResponse.deviceToUse = selectedDevice;
|
||||
mResponse.mustContinue = true;
|
||||
shell.close();
|
||||
mResponse.setDeviceToUse((Device)object);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// bottom part with the ok/cancel
|
||||
Composite bottomComp = new Composite(shell, SWT.NONE);
|
||||
bottomComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
// 3 items in the layout: createButton, spacer, composite with ok/cancel
|
||||
// (to force same width).
|
||||
bottomComp.setLayout(new GridLayout(3 /* numColums */, false /* makeColumnsEqualWidth */));
|
||||
|
||||
mCreateButton = new Button(bottomComp, SWT.NONE);
|
||||
mCreateButton.setText("Launch Emulator");
|
||||
mCreateButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
mResponse.mustContinue = true;
|
||||
mResponse.mustLaunchEmulator = true;
|
||||
shell.close();
|
||||
}
|
||||
});
|
||||
Button radio2 = new Button(top, SWT.RADIO);
|
||||
radio2.setText("Launch a new Virtual Device");
|
||||
|
||||
// offset the selector from the radio button
|
||||
offsetComp = new Composite(top, SWT.NONE);
|
||||
offsetComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
layout = new GridLayout(1, false);
|
||||
layout.marginRight = layout.marginHeight = 0;
|
||||
layout.marginLeft = 30;
|
||||
offsetComp.setLayout(layout);
|
||||
|
||||
// the spacer
|
||||
Composite spacer = new Composite(bottomComp, SWT.NONE);
|
||||
GridData gd;
|
||||
spacer.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||
gd.heightHint = 0;
|
||||
|
||||
// the composite to contain ok/cancel
|
||||
Composite buttonContainer = new Composite(bottomComp, SWT.NONE);
|
||||
GridLayout gl = new GridLayout(2 /* numColums */, true /* makeColumnsEqualWidth */);
|
||||
gl.marginHeight = gl.marginWidth = 0;
|
||||
buttonContainer.setLayout(gl);
|
||||
|
||||
mOkButton = new Button(buttonContainer, SWT.NONE);
|
||||
mOkButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
mOkButton.setEnabled(false);
|
||||
mOkButton.setText("OK");
|
||||
mOkButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
mResponse.mustContinue = true;
|
||||
shell.close();
|
||||
}
|
||||
});
|
||||
|
||||
Button cancelButton = new Button(buttonContainer, SWT.NONE);
|
||||
cancelButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
cancelButton.setText("Cancel");
|
||||
cancelButton.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
mResponse.mustContinue = false;
|
||||
shell.close();
|
||||
}
|
||||
});
|
||||
|
||||
mPreferredAvdSelector = new AvdSelector(offsetComp, getNonRunningAvds(), mProjectTarget,
|
||||
false /*allowMultipleSelection*/);
|
||||
mPreferredAvdSelector.setTableHeightHint(100);
|
||||
mPreferredAvdSelector.setEnabled(false);
|
||||
mDeviceTable.addSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
int count = mDeviceTable.getSelectionCount();
|
||||
if (count != 1) {
|
||||
handleSelection(null);
|
||||
} else {
|
||||
int index = mDeviceTable.getSelectionIndex();
|
||||
Object data = mViewer.getElementAt(index);
|
||||
if (data instanceof Device) {
|
||||
handleSelection((Device)data);
|
||||
} else {
|
||||
handleSelection(null);
|
||||
}
|
||||
handleDeviceSelection();
|
||||
}
|
||||
});
|
||||
|
||||
mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
if (mDisableAvdSelectionChange == false) {
|
||||
mResponse.setAvdToLaunch(mPreferredAvdSelector.getFirstSelected());
|
||||
enableOkButton();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mDeviceTable.setFocus();
|
||||
shell.setDefaultButton(mOkButton);
|
||||
|
||||
updateDefaultSelection();
|
||||
AndroidDebugBridge.addDeviceChangeListener(this);
|
||||
|
||||
return top;
|
||||
}
|
||||
|
||||
private void loadImages() {
|
||||
@@ -504,6 +498,10 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
|
||||
|
||||
// update the selection
|
||||
updateDefaultSelection();
|
||||
|
||||
// update the display of AvdInfo (since it's filtered to only display
|
||||
// non running AVD.)
|
||||
refillAvdList();
|
||||
} else {
|
||||
// table is disposed, we need to do something.
|
||||
// lets remove ourselves from the listener.
|
||||
@@ -546,23 +544,49 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
|
||||
|
||||
// update the defaultSelection.
|
||||
updateDefaultSelection();
|
||||
|
||||
|
||||
// update the display of AvdInfo (since it's filtered to only display
|
||||
// non running AVD). This is done on deviceChanged because the avd name
|
||||
// of a (emulator) device may be updated as the emulator boots.
|
||||
refillAvdList();
|
||||
|
||||
// if the changed device is the current selection,
|
||||
// we update the OK button based on its state.
|
||||
if (device == mResponse.deviceToUse) {
|
||||
mOkButton.setEnabled(mResponse.deviceToUse.isOnline());
|
||||
if (device == mResponse.getDeviceToUse()) {
|
||||
enableOkButton();
|
||||
}
|
||||
|
||||
} else {
|
||||
// table is disposed, we need to do something.
|
||||
// lets remove ourselves from the listener.
|
||||
AndroidDebugBridge.removeDeviceChangeListener(dialog);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the dialog is in "device" mode (true), or in "avd" mode (false).
|
||||
*/
|
||||
private boolean isDeviceMode() {
|
||||
return mDeviceRadioButton.getSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the OK button of the dialog based on various selections in the dialog.
|
||||
*/
|
||||
private void enableOkButton() {
|
||||
Button okButton = getButton(IDialogConstants.OK_ID);
|
||||
|
||||
if (isDeviceMode()) {
|
||||
okButton.setEnabled(mResponse.getDeviceToUse() != null &&
|
||||
mResponse.getDeviceToUse().isOnline());
|
||||
} else {
|
||||
okButton.setEnabled(mResponse.getAvdToLaunch() != null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the {@link Runnable} in the UI thread.
|
||||
* @param runnable the runnable to execute.
|
||||
@@ -577,16 +601,31 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDeviceSelection() {
|
||||
int count = mDeviceTable.getSelectionCount();
|
||||
if (count != 1) {
|
||||
handleSelection(null);
|
||||
} else {
|
||||
int index = mDeviceTable.getSelectionIndex();
|
||||
Object data = mViewer.getElementAt(index);
|
||||
if (data instanceof Device) {
|
||||
handleSelection((Device)data);
|
||||
} else {
|
||||
handleSelection(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSelection(Device device) {
|
||||
mResponse.deviceToUse = device;
|
||||
mOkButton.setEnabled(device != null && mResponse.deviceToUse.isOnline());
|
||||
mResponse.setDeviceToUse(device);
|
||||
enableOkButton();
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for a default device to select. This is done by looking for the running
|
||||
* clients on each device and finding one similar to the one being launched.
|
||||
* <p/>
|
||||
* This is done every time the device list changed, until there is a selection..
|
||||
* This is done every time the device list changed unless there is a already selection.
|
||||
*/
|
||||
private void updateDefaultSelection() {
|
||||
if (mDeviceTable.getSelectionCount() == 0) {
|
||||
@@ -599,8 +638,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
|
||||
|
||||
for (Client client : clients) {
|
||||
|
||||
if (mLaunchInfo.mPackageName.equals(
|
||||
client.getClientData().getClientDescription())) {
|
||||
if (mPackageName.equals(client.getClientData().getClientDescription())) {
|
||||
// found a match! Select it.
|
||||
mViewer.setSelection(new StructuredSelection(device));
|
||||
handleSelection(device);
|
||||
@@ -611,6 +649,57 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleDeviceSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of {@link AvdInfo} that are not already running in an emulator.
|
||||
*/
|
||||
private AvdInfo[] getNonRunningAvds() {
|
||||
ArrayList<AvdInfo> list = new ArrayList<AvdInfo>();
|
||||
|
||||
Device[] devices = AndroidDebugBridge.getBridge().getDevices();
|
||||
|
||||
// loop through all the Avd and put the one that are not running in the list.
|
||||
avdLoop: for (AvdInfo info : mFullAvdList) {
|
||||
for (Device d : devices) {
|
||||
if (info.getName().equals(d.getAvdName())) {
|
||||
continue avdLoop;
|
||||
}
|
||||
}
|
||||
list.add(info);
|
||||
}
|
||||
|
||||
return list.toArray(new AvdInfo[list.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refills the AVD list keeping the current selection.
|
||||
*/
|
||||
private void refillAvdList() {
|
||||
AvdInfo[] array = getNonRunningAvds();
|
||||
|
||||
// save the current selection
|
||||
AvdInfo selected = mPreferredAvdSelector.getFirstSelected();
|
||||
|
||||
// disable selection change.
|
||||
mDisableAvdSelectionChange = true;
|
||||
|
||||
// set the new list in the selector
|
||||
mPreferredAvdSelector.setAvds(array, mProjectTarget);
|
||||
|
||||
// attempt to reselect the proper avd if needed
|
||||
if (selected != null) {
|
||||
if (mPreferredAvdSelector.setSelection(selected) == false) {
|
||||
// looks like the selection is lost. this can happen if an emulator
|
||||
// running the AVD that was selected was launched from outside of Eclipse).
|
||||
mResponse.setAvdToLaunch(null);
|
||||
enableOkButton();
|
||||
}
|
||||
}
|
||||
|
||||
// enable the selection change
|
||||
mDisableAvdSelectionChange = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,8 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
|
||||
|
||||
private Button mNoBootAnimButton;
|
||||
|
||||
private Label mPreferredAvdLabel;
|
||||
|
||||
/**
|
||||
* Returns the emulator ready speed option value.
|
||||
* @param value The index of the combo selection.
|
||||
@@ -160,13 +162,26 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
updateLaunchConfigurationDialog();
|
||||
|
||||
boolean auto = mAutoTargetButton.getSelection();
|
||||
mPreferredAvdSelector.setEnabled(auto);
|
||||
mPreferredAvdLabel.setEnabled(auto);
|
||||
}
|
||||
});
|
||||
|
||||
new Label(targetModeGroup, SWT.NONE).setText("Preferred Android Virtual Device");
|
||||
Composite offsetComp = new Composite(targetModeGroup, SWT.NONE);
|
||||
offsetComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||
layout = new GridLayout(1, false);
|
||||
layout.marginRight = layout.marginHeight = 0;
|
||||
layout.marginLeft = 30;
|
||||
offsetComp.setLayout(layout);
|
||||
|
||||
mPreferredAvdLabel = new Label(offsetComp, SWT.NONE);
|
||||
mPreferredAvdLabel.setText("Select a preferred Android Virtual Device:");
|
||||
AvdInfo[] avds = new AvdInfo[0];
|
||||
mPreferredAvdSelector = new AvdSelector(targetModeGroup, avds,
|
||||
mPreferredAvdSelector = new AvdSelector(offsetComp, avds,
|
||||
false /*allowMultipleSelection*/);
|
||||
mPreferredAvdSelector.setTableHeightHint(100);
|
||||
mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() {
|
||||
@Override
|
||||
public void widgetSelected(SelectionEvent e) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.project.internal;
|
||||
|
||||
import com.android.ide.eclipse.adt.AdtConstants;
|
||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||
import com.android.ide.eclipse.adt.sdk.LoadStatus;
|
||||
import com.android.ide.eclipse.adt.sdk.Sdk;
|
||||
import com.android.ide.eclipse.common.project.BaseProjectHelper;
|
||||
@@ -44,8 +45,12 @@ import org.eclipse.jdt.core.IJavaProject;
|
||||
import org.eclipse.jdt.core.JavaCore;
|
||||
import org.eclipse.jdt.core.JavaModelException;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Classpath container initializer responsible for binding {@link AndroidClasspathContainer} to
|
||||
@@ -56,6 +61,21 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
|
||||
private final static String CONTAINER_ID =
|
||||
"com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"; //$NON-NLS-1$
|
||||
|
||||
/** path separator to store multiple paths in a single property. This is guaranteed to not
|
||||
* be in a path.
|
||||
*/
|
||||
private final static String PATH_SEPARATOR = "\u001C"; //$NON-NLS-1$
|
||||
|
||||
private final static String PROPERTY_CONTAINER_CACHE = "androidContainerCache"; //$NON-NLS-1$
|
||||
private final static String PROPERTY_TARGET_NAME = "androidTargetCache"; //$NON-NLS-1$
|
||||
private final static String CACHE_VERSION = "01"; //$NON-NLS-1$
|
||||
private final static String CACHE_VERSION_SEP = CACHE_VERSION + PATH_SEPARATOR;
|
||||
|
||||
private final static int PATH_ANDROID_JAR = 0;
|
||||
private final static int PATH_ANDROID_SRC = 1;
|
||||
private final static int PATH_ANDROID_DOCS = 2;
|
||||
private final static int PATH_ANDROID_OPT_DOCS = 3;
|
||||
|
||||
public AndroidClasspathContainerInitializer() {
|
||||
// pass
|
||||
}
|
||||
@@ -71,7 +91,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
|
||||
if (CONTAINER_ID.equals(containerPath.toString())) {
|
||||
JavaCore.setClasspathContainer(new Path(CONTAINER_ID),
|
||||
new IJavaProject[] { project },
|
||||
new IClasspathContainer[] { allocateAndroidContainer(CONTAINER_ID, project) },
|
||||
new IClasspathContainer[] { allocateAndroidContainer(project) },
|
||||
new NullProgressMonitor());
|
||||
}
|
||||
}
|
||||
@@ -111,7 +131,7 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
|
||||
|
||||
IClasspathContainer[] containers = new IClasspathContainer[projectCount];
|
||||
for (int i = 0 ; i < projectCount; i++) {
|
||||
containers[i] = allocateAndroidContainer(CONTAINER_ID, androidProjects[i]);
|
||||
containers[i] = allocateAndroidContainer(androidProjects[i]);
|
||||
}
|
||||
|
||||
// give each project their new container in one call.
|
||||
@@ -128,133 +148,180 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
|
||||
/**
|
||||
* Allocates and returns an {@link AndroidClasspathContainer} object with the proper
|
||||
* path to the framework jar file.
|
||||
* @param containerId the container id to be used.
|
||||
* @param javaProject The java project that will receive the container.
|
||||
*/
|
||||
private static IClasspathContainer allocateAndroidContainer(String containerId,
|
||||
IJavaProject javaProject) {
|
||||
private static IClasspathContainer allocateAndroidContainer(IJavaProject javaProject) {
|
||||
final IProject iProject = javaProject.getProject();
|
||||
|
||||
// remove potential MARKER_TARGETs.
|
||||
try {
|
||||
if (iProject.exists()) {
|
||||
iProject.deleteMarkers(AdtConstants.MARKER_TARGET, true,
|
||||
IResource.DEPTH_INFINITE);
|
||||
}
|
||||
} catch (CoreException ce) {
|
||||
// just log the error
|
||||
AdtPlugin.log(ce, "Error removing target marker.");
|
||||
}
|
||||
|
||||
// First we check if the SDK has been loaded.
|
||||
// By passing the javaProject to getSdkLoadStatus(), we ensure that, should the SDK
|
||||
// not be loaded yet, the classpath container will be resolved again once the SDK is loaded.
|
||||
boolean sdkIsLoaded = AdtPlugin.getDefault().getSdkLoadStatus(javaProject) ==
|
||||
LoadStatus.LOADED;
|
||||
|
||||
// then we check if the project has a valid target.
|
||||
IAndroidTarget target = null;
|
||||
if (sdkIsLoaded) {
|
||||
target = Sdk.getCurrent().getTarget(iProject);
|
||||
}
|
||||
|
||||
// if we are loaded and the target is non null, we create a valid ClassPathContainer
|
||||
if (sdkIsLoaded && target != null) {
|
||||
String targetName = null;
|
||||
if (target.isPlatform()) {
|
||||
targetName = target.getName();
|
||||
} else {
|
||||
targetName = String.format("%1$s (%2$s)", target.getName(),
|
||||
target.getApiVersionName());
|
||||
}
|
||||
|
||||
return new AndroidClasspathContainer(createFrameworkClasspath(target),
|
||||
new Path(containerId), targetName);
|
||||
}
|
||||
|
||||
// else we put a marker on the project, and return a dummy container (to replace the
|
||||
// previous one if there was one.)
|
||||
|
||||
// Get the project's target's hash string (if it exists)
|
||||
String hashString = Sdk.getProjectTargetHashString(iProject);
|
||||
|
||||
String message = null;
|
||||
String markerMessage = null;
|
||||
boolean outputToConsole = true;
|
||||
if (hashString == null || hashString.length() == 0) {
|
||||
// if there is no hash string we only show this if the SDK is loaded.
|
||||
// For a project opened at start-up with no target, this would be displayed twice,
|
||||
// once when the project is opened, and once after the SDK has finished loading.
|
||||
// By testing the sdk is loaded, we only show this once in the console.
|
||||
if (sdkIsLoaded) {
|
||||
message = String.format(
|
||||
"Project has no target set. Edit the project properties to set one.");
|
||||
}
|
||||
} else if (sdkIsLoaded) {
|
||||
message = String.format(
|
||||
"Unable to resolve target '%s'", hashString);
|
||||
} else {
|
||||
// this is the case where there is a hashString but the SDK is not yet
|
||||
// loaded and therefore we can't get the target yet.
|
||||
message = String.format(
|
||||
"Unable to resolve target '%s' until the SDK is loaded.", hashString);
|
||||
|
||||
// let's not log this one to the console as it will happen at every boot,
|
||||
// and it's expected. (we do keep the error marker though).
|
||||
outputToConsole = false;
|
||||
}
|
||||
|
||||
if (message != null) {
|
||||
// log the error and put the marker on the project if we can.
|
||||
if (outputToConsole) {
|
||||
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_ALWAYS, iProject, message);
|
||||
}
|
||||
try {
|
||||
AdtPlugin plugin = AdtPlugin.getDefault();
|
||||
|
||||
try {
|
||||
BaseProjectHelper.addMarker(iProject, AdtConstants.MARKER_TARGET, message, -1,
|
||||
IMarker.SEVERITY_ERROR, IMarker.PRIORITY_HIGH);
|
||||
} catch (CoreException e) {
|
||||
// In some cases, the workspace may be locked for modification when we pass here.
|
||||
// We schedule a new job to put the marker after.
|
||||
final String fmessage = message;
|
||||
Job markerJob = new Job("Android SDK: Resolving error markers") {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected IStatus run(IProgressMonitor monitor) {
|
||||
try {
|
||||
BaseProjectHelper.addMarker(iProject, AdtConstants.MARKER_TARGET,
|
||||
fmessage, -1, IMarker.SEVERITY_ERROR, IMarker.PRIORITY_HIGH);
|
||||
} catch (CoreException e2) {
|
||||
return e2.getStatus();
|
||||
}
|
||||
// get the lock object for project manipulation during SDK load.
|
||||
Object lock = plugin.getSdkLockObject();
|
||||
synchronized (lock) {
|
||||
boolean sdkIsLoaded = plugin.getSdkLoadStatus() == LoadStatus.LOADED;
|
||||
|
||||
// check if the project has a valid target.
|
||||
IAndroidTarget target = null;
|
||||
if (sdkIsLoaded) {
|
||||
target = Sdk.getCurrent().getTarget(iProject);
|
||||
}
|
||||
|
||||
return Status.OK_STATUS;
|
||||
// if we are loaded and the target is non null, we create a valid ClassPathContainer
|
||||
if (sdkIsLoaded && target != null) {
|
||||
String targetName = null;
|
||||
if (target.isPlatform()) {
|
||||
targetName = target.getName();
|
||||
} else {
|
||||
targetName = String.format("%1$s (%2$s)", target.getName(),
|
||||
target.getApiVersionName());
|
||||
}
|
||||
|
||||
return new AndroidClasspathContainer(
|
||||
createClasspathEntries(iProject, target, targetName),
|
||||
new Path(CONTAINER_ID), targetName);
|
||||
}
|
||||
|
||||
// In case of error, we'll try different thing to provide the best error message
|
||||
// possible.
|
||||
// Get the project's target's hash string (if it exists)
|
||||
String hashString = Sdk.getProjectTargetHashString(iProject);
|
||||
|
||||
if (hashString == null || hashString.length() == 0) {
|
||||
// if there is no hash string we only show this if the SDK is loaded.
|
||||
// For a project opened at start-up with no target, this would be displayed
|
||||
// twice, once when the project is opened, and once after the SDK has
|
||||
// finished loading.
|
||||
// By testing the sdk is loaded, we only show this once in the console.
|
||||
if (sdkIsLoaded) {
|
||||
markerMessage = String.format(
|
||||
"Project has no target set. Edit the project properties to set one.");
|
||||
}
|
||||
} else if (sdkIsLoaded) {
|
||||
markerMessage = String.format(
|
||||
"Unable to resolve target '%s'", hashString);
|
||||
} else {
|
||||
// this is the case where there is a hashString but the SDK is not yet
|
||||
// loaded and therefore we can't get the target yet.
|
||||
// We check if there is a cache of the needed information.
|
||||
AndroidClasspathContainer container = getContainerFromCache(iProject);
|
||||
|
||||
if (container == null) {
|
||||
// either the cache was wrong (ie folder does not exists anymore), or
|
||||
// there was no cache. In this case we need to make sure the project
|
||||
// is resolved again after the SDK is loaded.
|
||||
plugin.setProjectToResolve(javaProject);
|
||||
|
||||
markerMessage = String.format(
|
||||
"Unable to resolve target '%s' until the SDK is loaded.",
|
||||
hashString);
|
||||
|
||||
// let's not log this one to the console as it will happen at every boot,
|
||||
// and it's expected. (we do keep the error marker though).
|
||||
outputToConsole = false;
|
||||
|
||||
} else {
|
||||
// we created a container from the cache, so we register the project
|
||||
// to be checked for cache validity once the SDK is loaded
|
||||
plugin.setProjectToCheck(javaProject);
|
||||
|
||||
// and return the container
|
||||
return container;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// return a dummy container to replace the one we may have had before.
|
||||
// It'll be replaced by the real when if/when the target is resolved if/when the
|
||||
// SDK finishes loading.
|
||||
return new IClasspathContainer() {
|
||||
public IClasspathEntry[] getClasspathEntries() {
|
||||
return new IClasspathEntry[0];
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "Unable to get system library for the project";
|
||||
}
|
||||
|
||||
public int getKind() {
|
||||
return IClasspathContainer.K_DEFAULT_SYSTEM;
|
||||
}
|
||||
|
||||
public IPath getPath() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
} finally {
|
||||
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);
|
||||
}
|
||||
|
||||
try {
|
||||
BaseProjectHelper.addMarker(iProject, AdtConstants.MARKER_TARGET, markerMessage,
|
||||
-1, IMarker.SEVERITY_ERROR, IMarker.PRIORITY_HIGH);
|
||||
} catch (CoreException e) {
|
||||
// In some cases, the workspace may be locked for modification when we
|
||||
// pass here.
|
||||
// We schedule a new job to put the marker after.
|
||||
final String fmessage = markerMessage;
|
||||
Job markerJob = new Job("Android SDK: Resolving error markers") {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected IStatus run(IProgressMonitor monitor) {
|
||||
try {
|
||||
BaseProjectHelper.addMarker(iProject, AdtConstants.MARKER_TARGET,
|
||||
fmessage, -1, IMarker.SEVERITY_ERROR,
|
||||
IMarker.PRIORITY_HIGH);
|
||||
} catch (CoreException e2) {
|
||||
return e2.getStatus();
|
||||
}
|
||||
|
||||
// build jobs are run after other interactive jobs
|
||||
markerJob.setPriority(Job.BUILD);
|
||||
markerJob.schedule();
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
};
|
||||
|
||||
// build jobs are run after other interactive jobs
|
||||
markerJob.setPriority(Job.BUILD);
|
||||
markerJob.schedule();
|
||||
}
|
||||
} else {
|
||||
// no error, remove potential MARKER_TARGETs.
|
||||
try {
|
||||
if (iProject.exists()) {
|
||||
iProject.deleteMarkers(AdtConstants.MARKER_TARGET, true,
|
||||
IResource.DEPTH_INFINITE);
|
||||
}
|
||||
} catch (CoreException ce) {
|
||||
// In some cases, the workspace may be locked for modification when we pass
|
||||
// here, so we schedule a new job to put the marker after.
|
||||
Job markerJob = new Job("Android SDK: Resolving error markers") {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected IStatus run(IProgressMonitor monitor) {
|
||||
try {
|
||||
iProject.deleteMarkers(AdtConstants.MARKER_TARGET, true,
|
||||
IResource.DEPTH_INFINITE);
|
||||
} catch (CoreException e2) {
|
||||
return e2.getStatus();
|
||||
}
|
||||
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
};
|
||||
|
||||
// build jobs are run after other interactive jobs
|
||||
markerJob.setPriority(Job.BUILD);
|
||||
markerJob.schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return a dummy container to replace the one we may have had before.
|
||||
return new IClasspathContainer() {
|
||||
public IClasspathEntry[] getClasspathEntries() {
|
||||
return new IClasspathEntry[0];
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "Unable to get system library for the project";
|
||||
}
|
||||
|
||||
public int getKind() {
|
||||
return IClasspathContainer.K_DEFAULT_SYSTEM;
|
||||
}
|
||||
|
||||
public IPath getPath() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,21 +331,114 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
|
||||
* java doc directory. This is dynamically created when a project is opened,
|
||||
* and never saved in the project itself, so there's no risk of storing an
|
||||
* obsolete path.
|
||||
*
|
||||
* The method also stores the paths used to create the entries in the project persistent
|
||||
* properties. A new {@link AndroidClasspathContainer} can be created from the stored path
|
||||
* using the {@link #getContainerFromCache(IProject)} method.
|
||||
* @param project
|
||||
* @param target The target that contains the libraries.
|
||||
* @param targetName
|
||||
*/
|
||||
private static IClasspathEntry[] createFrameworkClasspath(IAndroidTarget target) {
|
||||
private static IClasspathEntry[] createClasspathEntries(IProject project,
|
||||
IAndroidTarget target, String targetName) {
|
||||
|
||||
// get the path from the target
|
||||
String[] paths = getTargetPaths(target);
|
||||
|
||||
// create the classpath entry from the paths
|
||||
IClasspathEntry[] entries = createClasspathEntriesFromPaths(paths);
|
||||
|
||||
// paths now contains all the path required to recreate the IClasspathEntry with no
|
||||
// target info. We encode them in a single string, with each path separated by
|
||||
// OS path separator.
|
||||
StringBuilder sb = new StringBuilder(CACHE_VERSION);
|
||||
for (String p : paths) {
|
||||
sb.append(PATH_SEPARATOR);
|
||||
sb.append(p);
|
||||
}
|
||||
|
||||
// store this in a project persistent property
|
||||
ProjectHelper.saveStringProperty(project, PROPERTY_CONTAINER_CACHE, sb.toString());
|
||||
ProjectHelper.saveStringProperty(project, PROPERTY_TARGET_NAME, targetName);
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an {@link AndroidClasspathContainer} from the project cache, if possible.
|
||||
*/
|
||||
private static AndroidClasspathContainer getContainerFromCache(IProject project) {
|
||||
// get the cached info from the project persistent properties.
|
||||
String cache = ProjectHelper.loadStringProperty(project, PROPERTY_CONTAINER_CACHE);
|
||||
String targetNameCache = ProjectHelper.loadStringProperty(project, PROPERTY_TARGET_NAME);
|
||||
if (cache == null || targetNameCache == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// the first 2 chars must match CACHE_VERSION. The 3rd char is the normal separator.
|
||||
if (cache.startsWith(CACHE_VERSION_SEP) == false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
cache = cache.substring(CACHE_VERSION_SEP.length());
|
||||
|
||||
// the cache contains multiple paths, separated by a character guaranteed to not be in
|
||||
// the path (\u001C).
|
||||
// The first 3 are for android.jar (jar, source, doc), the rest are for the optional
|
||||
// libraries and should contain at least one doc and a jar (if there are any libraries).
|
||||
// Therefore, the path count should be 3 or 5+
|
||||
String[] paths = cache.split(Pattern.quote(PATH_SEPARATOR));
|
||||
if (paths.length < 3 || paths.length == 4) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// now we check the paths actually exist.
|
||||
// There's an exception: If the source folder for android.jar does not exist, this is
|
||||
// not a problem, so we skip it.
|
||||
// Also paths[PATH_ANDROID_DOCS] is a URI to the javadoc, so we test it a bit differently.
|
||||
try {
|
||||
if (new File(paths[PATH_ANDROID_JAR]).exists() == false ||
|
||||
new File(new URI(paths[PATH_ANDROID_DOCS])).exists() == false) {
|
||||
return null;
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
return null;
|
||||
} finally {
|
||||
|
||||
}
|
||||
|
||||
for (int i = 3 ; i < paths.length; i++) {
|
||||
String path = paths[i];
|
||||
if (path.length() > 0) {
|
||||
File f = new File(path);
|
||||
if (f.exists() == false) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IClasspathEntry[] entries = createClasspathEntriesFromPaths(paths);
|
||||
|
||||
return new AndroidClasspathContainer(entries,
|
||||
new Path(CONTAINER_ID), targetNameCache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array of {@link IClasspathEntry} from a set of paths.
|
||||
* @see #getTargetPaths(IAndroidTarget)
|
||||
*/
|
||||
private static IClasspathEntry[] createClasspathEntriesFromPaths(String[] paths) {
|
||||
ArrayList<IClasspathEntry> list = new ArrayList<IClasspathEntry>();
|
||||
|
||||
// First, we create the IClasspathEntry for the framework.
|
||||
// now add the android framework to the class path.
|
||||
// create the path object.
|
||||
IPath android_lib = new Path(target.getPath(IAndroidTarget.ANDROID_JAR));
|
||||
IPath android_src = new Path(target.getPath(IAndroidTarget.SOURCES));
|
||||
|
||||
IPath android_lib = new Path(paths[PATH_ANDROID_JAR]);
|
||||
IPath android_src = new Path(paths[PATH_ANDROID_SRC]);
|
||||
|
||||
// create the java doc link.
|
||||
IClasspathAttribute cpAttribute = JavaCore.newClasspathAttribute(
|
||||
IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, AdtPlugin.getUrlDoc());
|
||||
IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME,
|
||||
paths[PATH_ANDROID_DOCS]);
|
||||
|
||||
// create the access rule to restrict access to classes in com.android.internal
|
||||
IAccessRule accessRule = JavaCore.newAccessRule(
|
||||
@@ -292,42 +452,184 @@ public class AndroidClasspathContainerInitializer extends ClasspathContainerInit
|
||||
new IClasspathAttribute[] { cpAttribute },
|
||||
false // not exported.
|
||||
);
|
||||
|
||||
|
||||
list.add(frameworkClasspathEntry);
|
||||
|
||||
// now deal with optional libraries
|
||||
if (paths.length >= 5) {
|
||||
String docPath = paths[PATH_ANDROID_OPT_DOCS];
|
||||
int i = 4;
|
||||
while (i < paths.length) {
|
||||
Path jarPath = new Path(paths[i++]);
|
||||
|
||||
IClasspathAttribute[] attributes = null;
|
||||
if (docPath.length() > 0) {
|
||||
attributes = new IClasspathAttribute[] {
|
||||
JavaCore.newClasspathAttribute(
|
||||
IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME,
|
||||
docPath)
|
||||
};
|
||||
}
|
||||
|
||||
IClasspathEntry entry = JavaCore.newLibraryEntry(
|
||||
jarPath,
|
||||
null, // source attachment path
|
||||
null, // default source attachment root path.
|
||||
null,
|
||||
attributes,
|
||||
false // not exported.
|
||||
);
|
||||
list.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return list.toArray(new IClasspathEntry[list.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the projects' caches. If the cache was valid, the project is removed from the list.
|
||||
* @param projects the list of projects to check.
|
||||
*/
|
||||
public static void checkProjectsCache(ArrayList<IJavaProject> projects) {
|
||||
int i = 0;
|
||||
projectLoop: while (i < projects.size()) {
|
||||
IJavaProject javaProject = projects.get(i);
|
||||
IProject iProject = javaProject.getProject();
|
||||
|
||||
// get the target from the project and its paths
|
||||
IAndroidTarget target = Sdk.getCurrent().getTarget(javaProject.getProject());
|
||||
if (target == null) {
|
||||
// this is really not supposed to happen. This would mean there are cached paths,
|
||||
// but default.properties was deleted. Keep the project in the list to force
|
||||
// a resolve which will display the error.
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] targetPaths = getTargetPaths(target);
|
||||
|
||||
// now get the cached paths
|
||||
String cache = ProjectHelper.loadStringProperty(iProject, PROPERTY_CONTAINER_CACHE);
|
||||
if (cache == null) {
|
||||
// this should not happen. We'll force resolve again anyway.
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] cachedPaths = cache.split(Pattern.quote(PATH_SEPARATOR));
|
||||
if (cachedPaths.length < 3 || cachedPaths.length == 4) {
|
||||
// paths length is wrong. simply resolve the project again
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now we compare the paths. The first 4 can be compared directly.
|
||||
// because of case sensitiveness we need to use File objects
|
||||
|
||||
if (targetPaths.length != cachedPaths.length) {
|
||||
// different paths, force resolve again.
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// compare the main paths (android.jar, main sources, main javadoc)
|
||||
if (new File(targetPaths[PATH_ANDROID_JAR]).equals(
|
||||
new File(cachedPaths[PATH_ANDROID_JAR])) == false ||
|
||||
new File(targetPaths[PATH_ANDROID_SRC]).equals(
|
||||
new File(cachedPaths[PATH_ANDROID_SRC])) == false ||
|
||||
new File(targetPaths[PATH_ANDROID_DOCS]).equals(
|
||||
new File(cachedPaths[PATH_ANDROID_DOCS])) == false) {
|
||||
// different paths, force resolve again.
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cachedPaths.length > PATH_ANDROID_OPT_DOCS) {
|
||||
// compare optional libraries javadoc
|
||||
if (new File(targetPaths[PATH_ANDROID_OPT_DOCS]).equals(
|
||||
new File(cachedPaths[PATH_ANDROID_OPT_DOCS])) == false) {
|
||||
// different paths, force resolve again.
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// testing the optional jar files is a little bit trickier.
|
||||
// The order is not guaranteed to be identical.
|
||||
// From a previous test, we do know however that there is the same number.
|
||||
// The number of libraries should be low enough that we can simply go through the
|
||||
// lists manually.
|
||||
targetLoop: for (int tpi = 4 ; tpi < targetPaths.length; tpi++) {
|
||||
String targetPath = targetPaths[tpi];
|
||||
|
||||
// look for a match in the other array
|
||||
for (int cpi = 4 ; cpi < cachedPaths.length; cpi++) {
|
||||
if (new File(targetPath).equals(new File(cachedPaths[cpi]))) {
|
||||
// found a match. Try the next targetPath
|
||||
continue targetLoop;
|
||||
}
|
||||
}
|
||||
|
||||
// if we stop here, we haven't found a match, which means there's a
|
||||
// discrepancy in the libraries. We force a resolve.
|
||||
i++;
|
||||
continue projectLoop;
|
||||
}
|
||||
}
|
||||
|
||||
// at the point the check passes, and we can remove the project from the list.
|
||||
// we do not increment i in this case.
|
||||
projects.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paths necessary to create the {@link IClasspathEntry} for this targets.
|
||||
* <p/>The paths are always in the same order.
|
||||
* <ul>
|
||||
* <li>Path to android.jar</li>
|
||||
* <li>Path to the source code for android.jar</li>
|
||||
* <li>Path to the javadoc for the android platform</li>
|
||||
* </ul>
|
||||
* Additionally, if there are optional libraries, the array will contain:
|
||||
* <ul>
|
||||
* <li>Path to the librairies javadoc</li>
|
||||
* <li>Path to the first .jar file</li>
|
||||
* <li>(more .jar as needed)</li>
|
||||
* </ul>
|
||||
*/
|
||||
private static String[] getTargetPaths(IAndroidTarget target) {
|
||||
ArrayList<String> paths = new ArrayList<String>();
|
||||
|
||||
// first, we get the path for android.jar
|
||||
// The order is: android.jar, source folder, docs folder
|
||||
paths.add(target.getPath(IAndroidTarget.ANDROID_JAR));
|
||||
paths.add(target.getPath(IAndroidTarget.SOURCES));
|
||||
paths.add(AdtPlugin.getUrlDoc());
|
||||
|
||||
// now deal with optional libraries.
|
||||
IOptionalLibrary[] libraries = target.getOptionalLibraries();
|
||||
if (libraries != null) {
|
||||
// all the optional libraries use the same javadoc, so we start with this
|
||||
String targetDocPath = target.getPath(IAndroidTarget.DOCS);
|
||||
if (targetDocPath != null) {
|
||||
paths.add(targetDocPath);
|
||||
} else {
|
||||
// we add an empty string, to always have the same count.
|
||||
paths.add("");
|
||||
}
|
||||
|
||||
// because different libraries could use the same jar file, we make sure we add
|
||||
// each jar file only once.
|
||||
HashSet<String> visitedJars = new HashSet<String>();
|
||||
for (IOptionalLibrary library : libraries) {
|
||||
String jarPath = library.getJarPath();
|
||||
if (visitedJars.contains(jarPath) == false) {
|
||||
visitedJars.add(jarPath);
|
||||
|
||||
// create the java doc link, if needed
|
||||
String targetDocPath = target.getPath(IAndroidTarget.DOCS);
|
||||
IClasspathAttribute[] attributes = null;
|
||||
if (targetDocPath != null) {
|
||||
attributes = new IClasspathAttribute[] {
|
||||
JavaCore.newClasspathAttribute(
|
||||
IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME,
|
||||
targetDocPath)
|
||||
};
|
||||
}
|
||||
|
||||
IClasspathEntry entry = JavaCore.newLibraryEntry(
|
||||
new Path(library.getJarPath()),
|
||||
null, // source attachment path
|
||||
null, // default source attachment root path.
|
||||
null,
|
||||
attributes,
|
||||
false // not exported.
|
||||
);
|
||||
list.add(entry);
|
||||
paths.add(jarPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list.toArray(new IClasspathEntry[list.size()]);
|
||||
return paths.toArray(new String[paths.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1691,7 +1691,7 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette
|
||||
// In this case data could be null, but this is not an error.
|
||||
// We can just silently return, as all the opened editors are automatically
|
||||
// refreshed once the SDK finishes loading.
|
||||
if (AdtPlugin.getDefault().getSdkLoadStatus(null) != LoadStatus.LOADING) {
|
||||
if (AdtPlugin.getDefault().getSdkLoadStatus() != LoadStatus.LOADING) {
|
||||
showErrorInEditor(String.format(
|
||||
"The project target (%s) was not properly loaded.",
|
||||
target.getName()));
|
||||
|
||||
Reference in New Issue
Block a user