Merge change I94f75699
* changes:
Reimport translations.
diff --git a/apps/Term/res/values/strings.xml b/apps/Term/res/values/strings.xml
index e3f8fcf..b5d622b 100644
--- a/apps/Term/res/values/strings.xml
+++ b/apps/Term/res/values/strings.xml
@@ -25,7 +25,7 @@
<string name="text_preferences">Text</string>
<string name="title_fontsize_preference">Font size</string>
- <string name="summary_fontsize_preference">Choose character height in pixels.</string>
+ <string name="summary_fontsize_preference">Choose character height in points.</string>
<string name="dialog_title_fontsize_preference">Font size</string>
<string name="title_color_preference">Colors</string>
diff --git a/apps/Term/src/com/android/term/Term.java b/apps/Term/src/com/android/term/Term.java
index 6041baf..cbf94cf 100644
--- a/apps/Term/src/com/android/term/Term.java
+++ b/apps/Term/src/com/android/term/Term.java
@@ -45,6 +45,7 @@
import android.os.Message;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.GestureDetector;
import android.view.KeyEvent;
@@ -158,14 +159,6 @@
super.onCreate(icicle);
Log.e(Term.LOG_TAG, "onCreate");
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
- mPrefs.registerOnSharedPreferenceChangeListener(
- new SharedPreferences.OnSharedPreferenceChangeListener(){
-
- public void onSharedPreferenceChanged(
- SharedPreferences sharedPreferences, String key) {
- readPrefs();
- updatePrefs();
- }});
readPrefs();
setContentView(R.layout.term_activity);
@@ -227,7 +220,7 @@
private void sendInitialCommand() {
String initialCommand = mInitialCommand;
- if (initialCommand == null) {
+ if (initialCommand == null || initialCommand.equals("")) {
initialCommand = DEFAULT_INITIAL_COMMAND;
}
if (initialCommand.length() > 0) {
@@ -253,7 +246,7 @@
private void createSubprocess(int[] processId) {
String shell = mShell;
- if (shell == null) {
+ if (shell == null || shell.equals("")) {
shell = DEFAULT_SHELL;
}
ArrayList<String> args = parse(shell);
@@ -347,7 +340,9 @@
}
private void updatePrefs() {
- mEmulatorView.setTextSize(mFontSize);
+ DisplayMetrics metrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(metrics);
+ mEmulatorView.setTextSize((int) (mFontSize * metrics.density));
setColors();
mControlKeyCode = CONTROL_KEY_SCHEMES[mControlKeyId];
}
@@ -369,17 +364,10 @@
}
@Override
- public void onPause() {
- SharedPreferences.Editor e = mPrefs.edit();
- e.clear();
- e.putString(FONTSIZE_KEY, Integer.toString(mFontSize));
- e.putString(COLOR_KEY, Integer.toString(mColorId));
- e.putString(CONTROLKEY_KEY, Integer.toString(mControlKeyId));
- e.putString(SHELL_KEY, mShell);
- e.putString(INITIALCOMMAND_KEY, mInitialCommand);
- e.commit();
-
- super.onPause();
+ public void onResume() {
+ super.onResume();
+ readPrefs();
+ updatePrefs();
}
@Override
diff --git a/samples/BrowserPlugin/jni/PluginObject.cpp b/samples/BrowserPlugin/jni/PluginObject.cpp
index 80f5e7c..7d92f7d 100644
--- a/samples/BrowserPlugin/jni/PluginObject.cpp
+++ b/samples/BrowserPlugin/jni/PluginObject.cpp
@@ -35,6 +35,16 @@
#include "main.h"
#include "PluginObject.h"
+int SubPlugin::getPluginWidth() {
+ PluginObject *obj = (PluginObject*) inst()->pdata;
+ return obj->window->width;
+}
+
+int SubPlugin::getPluginHeight() {
+ PluginObject *obj = (PluginObject*) inst()->pdata;
+ return obj->window->height;
+}
+
static void pluginInvalidate(NPObject *obj);
static bool pluginHasProperty(NPObject *obj, NPIdentifier name);
static bool pluginHasMethod(NPObject *obj, NPIdentifier name);
diff --git a/samples/BrowserPlugin/jni/PluginObject.h b/samples/BrowserPlugin/jni/PluginObject.h
index deb60ea..21b7707 100644
--- a/samples/BrowserPlugin/jni/PluginObject.h
+++ b/samples/BrowserPlugin/jni/PluginObject.h
@@ -44,6 +44,9 @@
virtual int16 handleEvent(const ANPEvent* evt) = 0;
virtual bool supportsDrawingModel(ANPDrawingModel) = 0;
+ int getPluginWidth();
+ int getPluginHeight();
+
NPP inst() const { return m_inst; }
private:
diff --git a/samples/BrowserPlugin/jni/jni-bridge.cpp b/samples/BrowserPlugin/jni/jni-bridge.cpp
index 22e76da..08e7f5a 100644
--- a/samples/BrowserPlugin/jni/jni-bridge.cpp
+++ b/samples/BrowserPlugin/jni/jni-bridge.cpp
@@ -51,6 +51,16 @@
}
}
+static jint getSurfaceWidth(JNIEnv* env, jobject thiz, jint npp) {
+ SurfaceSubPlugin* obj = getPluginObject(npp);
+ return obj->getPluginWidth();
+}
+
+static jint getSurfaceHeight(JNIEnv* env, jobject thiz, jint npp) {
+ SurfaceSubPlugin* obj = getPluginObject(npp);
+ return obj->getPluginHeight();
+}
+
static jboolean isFixedSurface(JNIEnv* env, jobject thiz, jint npp) {
SurfaceSubPlugin* obj = getPluginObject(npp);
return obj->isFixedSurface();
@@ -63,6 +73,8 @@
{ "nativeSurfaceCreated", "(ILandroid/view/View;)V", (void*) surfaceCreated },
{ "nativeSurfaceChanged", "(IIII)V", (void*) surfaceChanged },
{ "nativeSurfaceDestroyed", "(I)V", (void*) surfaceDestroyed },
+ { "nativeGetSurfaceWidth", "(I)I", (void*) getSurfaceWidth },
+ { "nativeGetSurfaceHeight", "(I)I", (void*) getSurfaceHeight },
{ "nativeIsFixedSurface", "(I)Z", (void*) isFixedSurface },
};
diff --git a/samples/BrowserPlugin/src/com/android/sampleplugin/SamplePluginStub.java b/samples/BrowserPlugin/src/com/android/sampleplugin/SamplePluginStub.java
index 3c0a0c7..22b7b44 100644
--- a/samples/BrowserPlugin/src/com/android/sampleplugin/SamplePluginStub.java
+++ b/samples/BrowserPlugin/src/com/android/sampleplugin/SamplePluginStub.java
@@ -63,10 +63,12 @@
}
});
-
+
+ // TODO provide way for native plugin code to reset the size
if (nativeIsFixedSurface(npp)) {
- //TODO get the fixed dimensions from the plugin
- //view.getHolder().setFixedSize(width, height);
+ int width = nativeGetSurfaceWidth(npp);
+ int height = nativeGetSurfaceHeight(npp);
+ view.getHolder().setFixedSize(width, height);
}
return view;
@@ -114,5 +116,7 @@
private native void nativeSurfaceCreated(int npp, View surfaceView);
private native void nativeSurfaceChanged(int npp, int format, int width, int height);
private native void nativeSurfaceDestroyed(int npp);
+ private native int nativeGetSurfaceWidth(int npp);
+ private native int nativeGetSurfaceHeight(int npp);
private native boolean nativeIsFixedSurface(int npp);
}
diff --git a/testrunner/test_defs.xml b/testrunner/test_defs.xml
index adb854e..42eae3a 100644
--- a/testrunner/test_defs.xml
+++ b/testrunner/test_defs.xml
@@ -72,8 +72,7 @@
<test name="apidemos"
build_path="development/samples/ApiDemos"
package="com.example.android.apis.tests"
- coverage_target="ApiDemos"
- continuous="true" />
+ coverage_target="ApiDemos" />
<test name="launchperf"
build_path="development/apps/launchperf"
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/RawImage.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/RawImage.java
index 6132bd0..3ec6148 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/RawImage.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/RawImage.java
@@ -186,7 +186,12 @@
int r = ((value >>> red_offset) & getMask(red_length)) << (8 - red_length);
int g = ((value >>> green_offset) & getMask(green_length)) << (8 - green_length);
int b = ((value >>> blue_offset) & getMask(blue_length)) << (8 - blue_length);
- int a = ((value >>> alpha_offset) & getMask(alpha_length)) << (8 - alpha_length);
+ int a;
+ if (alpha_length == 0) {
+ a = 0xFF; // force alpha to opaque if there's no alpha value in the framebuffer.
+ } else {
+ a = ((value >>> alpha_offset) & getMask(alpha_length)) << (8 - alpha_length);
+ }
return a << 24 | r << 16 | g << 8 | b;
}
diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/TableHelper.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/TableHelper.java
index f8d457e..9d557e0 100644
--- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/TableHelper.java
+++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/TableHelper.java
@@ -146,4 +146,58 @@
}
}
+ /**
+ * Create a TreeColumn with the specified parameters. If a
+ * <code>PreferenceStore</code> object and a preference entry name String
+ * object are provided then the column will listen to change in its width
+ * and update the preference store accordingly.
+ *
+ * @param parent The Table parent object
+ * @param header The header string
+ * @param style The column style
+ * @param width the width of the column if the preference value is missing
+ * @param pref_name The preference entry name for column width
+ * @param prefs The preference store
+ */
+ public static void createTreeColumn(Tree parent, String header, int style,
+ int width, final String pref_name,
+ final IPreferenceStore prefs) {
+
+ // create the column
+ TreeColumn col = new TreeColumn(parent, style);
+
+ // if there is no pref store or the entry is missing, we use the sample
+ // text and pack the column.
+ // Otherwise we just read the width from the prefs and apply it.
+ if (prefs == null || prefs.contains(pref_name) == false) {
+ col.setWidth(width);
+
+ // init the prefs store with the current value
+ if (prefs != null) {
+ prefs.setValue(pref_name, width);
+ }
+ } else {
+ col.setWidth(prefs.getInt(pref_name));
+ }
+
+ // set the header
+ col.setText(header);
+
+ // if there is a pref store and a pref entry name, then we setup a
+ // listener to catch column resize to put store the new width value.
+ if (prefs != null && pref_name != null) {
+ col.addControlListener(new ControlListener() {
+ public void controlMoved(ControlEvent e) {
+ }
+
+ public void controlResized(ControlEvent e) {
+ // get the new width
+ int w = ((TreeColumn)e.widget).getWidth();
+
+ // store in pref store
+ prefs.setValue(pref_name, w);
+ }
+ });
+ }
+ }
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
index 047c985..a52599f 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
@@ -1029,10 +1029,7 @@
final IAndroidTarget[] targets = sdk.getTargets();
final int n = targets.length;
if (n > 0) {
- // load the layout devices.
- sdk.parseAddOnLayoutDevices();
-
- // load the rest of the targes.
+ // load the rest of the targets.
// TODO: make this on-demand.
int w = 60 / n;
for (IAndroidTarget target : targets) {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalEditorPart.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalEditorPart.java
index f6fa0ea..302d3db 100755
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalEditorPart.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalEditorPart.java
@@ -19,6 +19,7 @@
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutReloadMonitor.ILayoutReloadListener;
import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite;
+import com.android.ide.eclipse.adt.internal.editors.layout.configuration.LayoutCreatorDialog;
import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite.IConfigListener;
import com.android.ide.eclipse.adt.internal.editors.layout.parts.ElementCreateCommand;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java
index 4f4ebb1..1d9054c 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java
@@ -21,6 +21,7 @@
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor.UiEditorActions;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutReloadMonitor.ILayoutReloadListener;
import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite;
+import com.android.ide.eclipse.adt.internal.editors.layout.configuration.LayoutCreatorDialog;
import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite.IConfigListener;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.parts.ElementCreateCommand;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigEditDialog.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigEditDialog.java
new file mode 100644
index 0000000..ca37786
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigEditDialog.java
@@ -0,0 +1,295 @@
+package com.android.ide.eclipse.adt.internal.editors.layout.configuration;
+
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
+import com.android.ide.eclipse.adt.internal.resources.configurations.LanguageQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.RegionQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.ResourceQualifier;
+import com.android.ide.eclipse.adt.internal.resources.configurations.VersionQualifier;
+import com.android.ide.eclipse.adt.internal.sdk.LayoutDevice;
+import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector;
+import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.ConfigurationState;
+import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.IQualifierFilter;
+import com.android.sdkuilib.ui.GridDialog;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.events.VerifyListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+import java.util.regex.Pattern;
+
+/**
+ * Dialog to edit both a {@link LayoutDevice}, and a {@link FolderConfiguration} at the same time.
+ */
+public class ConfigEditDialog extends GridDialog {
+
+ private static final Pattern FLOAT_PATTERN = Pattern.compile("\\d*(\\.\\d?)?");
+
+
+ private final FolderConfiguration mConfig = new FolderConfiguration();
+
+ private ConfigurationSelector mConfigSelector;
+ private Composite mStatusComposite;
+ private Label mStatusLabel;
+ private Label mStatusImage;
+
+ private Image mError;
+
+ private String mDeviceName;
+ private String mConfigName;
+ private float mXDpi = 0f;
+ private float mYDpi = 0f;
+
+
+ public ConfigEditDialog(Shell parentShell, FolderConfiguration config) {
+ super(parentShell, 1, false);
+ mConfig.set(config);
+ }
+
+ public void setDeviceName(String name) {
+ mDeviceName = name;
+ }
+
+ public String getDeviceName() {
+ return mDeviceName;
+ }
+
+ public void setXDpi(float xdpi) {
+ mXDpi = xdpi;
+ }
+
+ public float getXDpi() {
+ return mXDpi;
+ }
+
+ public void setYDpi(float ydpi) {
+ mYDpi = ydpi;
+ }
+
+ public float getYDpi() {
+ return mYDpi;
+ }
+
+ public void setConfigName(String name) {
+ mConfigName = name;
+ }
+
+ public String getConfigName() {
+ return mConfigName;
+ }
+
+ public void setConfig(FolderConfiguration config) {
+ mConfig.set(config);
+ }
+
+ public void getConfig(FolderConfiguration config) {
+ config.set(mConfig);
+ }
+
+ @Override
+ public void createDialogContent(Composite parent) {
+ mError = IconFactory.getInstance().getIcon("error"); //$NON-NLS-1$
+
+ Group deviceGroup = new Group(parent, SWT.NONE);
+ deviceGroup.setText("Device");
+ deviceGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
+ deviceGroup.setLayout(new GridLayout(2, false));
+
+ Label l = new Label(deviceGroup, SWT.None);
+ l.setText("Name");
+
+ final Text deviceNameText = new Text(deviceGroup, SWT.BORDER);
+ deviceNameText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ if (mDeviceName != null) {
+ deviceNameText.setText(mDeviceName);
+ }
+ deviceNameText.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ mDeviceName = deviceNameText.getText().trim();
+ validateOk();
+ }
+ });
+
+
+ VerifyListener floatVerifier = new VerifyListener() {
+ public void verifyText(VerifyEvent event) {
+ // combine the current content and the new text
+ String text = ((Text)event.widget).getText();
+ text = text.substring(0, event.start) + event.text + text.substring(event.end);
+
+ // now make sure it's a match for the regex
+ event.doit = FLOAT_PATTERN.matcher(text).matches();
+ }
+ };
+
+ l = new Label(deviceGroup, SWT.None);
+ l.setText("x dpi");
+
+ final Text deviceXDpiText = new Text(deviceGroup, SWT.BORDER);
+ deviceXDpiText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ if (mXDpi != 0f) {
+ deviceXDpiText.setText(String.format("%.1f", mXDpi)); //$NON-NLS-1$
+ }
+ deviceXDpiText.addVerifyListener(floatVerifier);
+ deviceXDpiText.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ mXDpi = Float.parseFloat(deviceXDpiText.getText());
+ }
+ });
+
+ l = new Label(deviceGroup, SWT.None);
+ l.setText("y dpi");
+
+ final Text deviceYDpiText = new Text(deviceGroup, SWT.BORDER);
+ deviceYDpiText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ if (mYDpi != 0f) {
+ deviceYDpiText.setText(String.format("%.1f", mYDpi)); //$NON-NLS-1$
+ }
+ deviceYDpiText.addVerifyListener(floatVerifier);
+ deviceYDpiText.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ mYDpi = Float.parseFloat(deviceYDpiText.getText());
+ }
+ });
+
+ Group configGroup = new Group(parent, SWT.NONE);
+ configGroup.setText("Configuration");
+ configGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
+ configGroup.setLayout(new GridLayout(2, false));
+
+ l = new Label(configGroup, SWT.None);
+ l.setText("Name");
+
+ final Text configNameText = new Text(configGroup, SWT.BORDER);
+ configNameText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ if (mConfigName != null) {
+ configNameText.setText(mConfigName);
+ }
+ configNameText.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ mConfigName = configNameText.getText().trim();
+ validateOk();
+ }
+ });
+
+ mConfigSelector = new ConfigurationSelector(configGroup);
+ // configure the selector to be in "device mode" and not accept language/region/version
+ // since those are selected from a different combo
+ // FIXME: add version combo.
+ mConfigSelector.setDeviceMode(true);
+ mConfigSelector.setQualifierFilter(new IQualifierFilter() {
+ public boolean accept(ResourceQualifier qualifier) {
+ if (qualifier instanceof LanguageQualifier ||
+ qualifier instanceof RegionQualifier ||
+ qualifier instanceof VersionQualifier) {
+ return false;
+ }
+
+ return true;
+ }
+ });
+ mConfigSelector.setConfiguration(mConfig);
+ GridData gd;
+ mConfigSelector.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
+ gd.horizontalSpan = 2;
+ gd.widthHint = ConfigurationSelector.WIDTH_HINT;
+ gd.heightHint = ConfigurationSelector.HEIGHT_HINT;
+
+ // add a listener to check on the validity of the FolderConfiguration as
+ // they are built.
+ mConfigSelector.setOnChangeListener(new Runnable() {
+ public void run() {
+ if (mConfigSelector.getState() == ConfigurationState.OK) {
+ mConfigSelector.getConfiguration(mConfig);
+ }
+
+ validateOk();
+ }
+ });
+
+ mStatusComposite = new Composite(parent, SWT.NONE);
+ mStatusComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ GridLayout gl = new GridLayout(2, false);
+ mStatusComposite.setLayout(gl);
+ gl.marginHeight = gl.marginWidth = 0;
+
+ mStatusImage = new Label(mStatusComposite, SWT.NONE);
+ mStatusLabel = new Label(mStatusComposite, SWT.NONE);
+ mStatusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ resetStatus();
+ }
+
+ @Override
+ protected Control createContents(Composite parent) {
+ Control c = super.createContents(parent);
+ validateOk();
+ return c;
+ }
+
+ /**
+ * resets the status label to show the file that will be created.
+ */
+ private void resetStatus() {
+ String displayString = Dialog.shortenText(
+ String.format("Config: %1$s", mConfig.toString()),
+ mStatusLabel);
+ mStatusLabel.setText(displayString);
+ }
+
+ private void setError(String text) {
+ String displayString = Dialog.shortenText(text, mStatusLabel);
+ mStatusLabel.setText(displayString);
+ mStatusImage.setImage(mError);
+ getButton(IDialogConstants.OK_ID).setEnabled(false);
+ }
+
+ private void validateOk() {
+ // check the device name
+ if (mDeviceName == null || mDeviceName.length() == 0) {
+ setError("Device name must not be empty");
+ return;
+ }
+
+ // check the config name
+ if (mConfigName == null || mConfigName.length() == 0) {
+ setError("Configuration name must not be empty");
+ return;
+ }
+
+ // and check the config itself
+ ConfigurationState state = mConfigSelector.getState();
+
+ switch (state) {
+ case INVALID_CONFIG:
+ ResourceQualifier invalidQualifier = mConfigSelector.getInvalidQualifier();
+ setError(String.format(
+ "Invalid Configuration: %1$s has no filter set.",
+ invalidQualifier.getName()));
+ return;
+ case REGION_WITHOUT_LANGUAGE:
+ setError("The Region qualifier requires the Language qualifier.");
+ return;
+ }
+
+ // no error
+ mStatusImage.setImage(null);
+ resetStatus();
+ getButton(IDialogConstants.OK_ID).setEnabled(true);
+
+ // need to relayout, because of the change in size in mErrorImage.
+ mStatusComposite.layout();
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigManagerDialog.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigManagerDialog.java
new file mode 100644
index 0000000..6e003a2
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigManagerDialog.java
@@ -0,0 +1,552 @@
+/*
+ * 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.internal.editors.layout.configuration;
+
+import com.android.ddmuilib.TableHelper;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
+import com.android.ide.eclipse.adt.internal.sdk.LayoutDevice;
+import com.android.ide.eclipse.adt.internal.sdk.LayoutDeviceManager;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdkuilib.ui.GridDialog;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ITableLabelProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+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.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Tree;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Dialog to view the layout devices with action button to create/edit/delete/copy layout devices
+ * and configs.
+ *
+ */
+public class ConfigManagerDialog extends GridDialog {
+
+ private final static String COL_NAME = AdtPlugin.PLUGIN_ID + ".configmanager.name"; //$NON-NLS-1$
+ private final static String COL_CONFIG = AdtPlugin.PLUGIN_ID + ".configmanager.config"; //$NON-NLS-1$
+
+ /**
+ * enum to represent the different origin of the layout devices.
+ */
+ private static enum DeviceType {
+ DEFAULT("Default"),
+ ADDON("Add-on"),
+ CUSTOM("Custom");
+
+ private final String mDisplay;
+
+ DeviceType(String display) {
+ mDisplay = display;
+ }
+
+ String getDisplayString() {
+ return mDisplay;
+ }
+ }
+
+ /**
+ * simple class representing the tree selection with the proper types.
+ */
+ private static class DeviceSelection {
+ public DeviceSelection(DeviceType type, LayoutDevice device,
+ Entry<String, FolderConfiguration> entry) {
+ this.type = type;
+ this.device = device;
+ this.entry = entry;
+ }
+
+ final DeviceType type;
+ final LayoutDevice device;
+ final Entry<String, FolderConfiguration> entry;
+ }
+
+ private final LayoutDeviceManager mManager;
+
+ private TreeViewer mTreeViewer;
+ private Button mNewButton;
+ private Button mEditButton;
+ private Button mCopyButton;
+ private Button mDeleteButton;
+
+ /**
+ * Content provider of the {@link TreeViewer}. The expected input is
+ * {@link LayoutDeviceManager}.
+ *
+ */
+ private final static class DeviceContentProvider implements ITreeContentProvider {
+ private final static DeviceType[] sCategory = new DeviceType[] {
+ DeviceType.DEFAULT, DeviceType.ADDON, DeviceType.CUSTOM
+ };
+
+ private LayoutDeviceManager mLayoutDeviceManager;
+
+ public DeviceContentProvider() {
+ }
+
+ public Object[] getElements(Object inputElement) {
+ return sCategory;
+ }
+
+ public Object[] getChildren(Object parentElement) {
+ if (parentElement instanceof DeviceType) {
+ if (DeviceType.DEFAULT.equals(parentElement)) {
+ return mLayoutDeviceManager.getDefaultLayoutDevices().toArray();
+ } else if (DeviceType.ADDON.equals(parentElement)) {
+ return mLayoutDeviceManager.getAddOnLayoutDevice().toArray();
+ } else if (DeviceType.CUSTOM.equals(parentElement)) {
+ return mLayoutDeviceManager.getUserLayoutDevices().toArray();
+ }
+ } else if (parentElement instanceof LayoutDevice) {
+ LayoutDevice device = (LayoutDevice)parentElement;
+ return device.getConfigs().entrySet().toArray();
+ }
+
+ return null;
+ }
+
+ public Object getParent(Object element) {
+ // parent cannot be computed. this is fine.
+ return null;
+ }
+
+ public boolean hasChildren(Object element) {
+ if (element instanceof DeviceType) {
+ if (DeviceType.DEFAULT.equals(element)) {
+ return mLayoutDeviceManager.getDefaultLayoutDevices().size() > 0;
+ } else if (DeviceType.ADDON.equals(element)) {
+ return mLayoutDeviceManager.getAddOnLayoutDevice().size() > 0;
+ } else if (DeviceType.CUSTOM.equals(element)) {
+ return mLayoutDeviceManager.getUserLayoutDevices().size() > 0;
+ }
+ } else if (element instanceof LayoutDevice) {
+ LayoutDevice device = (LayoutDevice)element;
+ return device.getConfigs().size() > 0;
+ }
+
+ return false;
+ }
+
+
+ public void dispose() {
+ // nothing to dispose
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ if (newInput instanceof LayoutDeviceManager) {
+ mLayoutDeviceManager = (LayoutDeviceManager)newInput;
+ return;
+ }
+
+ // when the dialog closes we get null input
+ if (newInput != null) {
+ throw new IllegalArgumentException(
+ "ConfigContentProvider requires input to be LayoutDeviceManager");
+ }
+ }
+ }
+
+ /**
+ * Label provider for the {@link TreeViewer}.
+ * Supported elements are {@link DeviceType}, {@link LayoutDevice}, and {@link Entry} (where
+ * the key is a {@link String} object, and the value is a {@link FolderConfiguration} object).
+ *
+ */
+ private final static class DeviceLabelProvider implements ITableLabelProvider {
+
+ public String getColumnText(Object element, int columnIndex) {
+ if (element instanceof DeviceType) {
+ if (columnIndex == 0) {
+ return ((DeviceType)element).getDisplayString();
+ }
+ } else if (element instanceof LayoutDevice) {
+ if (columnIndex == 0) {
+ return ((LayoutDevice)element).getName();
+ }
+ } else if (element instanceof Entry<?, ?>) {
+ if (columnIndex == 0) {
+ return (String)((Entry<?,?>)element).getKey();
+ } else {
+ return ((Entry<?,?>)element).getValue().toString();
+ }
+ }
+ return null;
+ }
+
+ public Image getColumnImage(Object element, int columnIndex) {
+ // no image
+ return null;
+ }
+
+ public void addListener(ILabelProviderListener listener) {
+ // no listener
+ }
+
+ public void removeListener(ILabelProviderListener listener) {
+ // no listener
+ }
+
+ public void dispose() {
+ // nothing to dispose
+ }
+
+ public boolean isLabelProperty(Object element, String property) {
+ return false;
+ }
+ }
+
+ protected ConfigManagerDialog(Shell parentShell) {
+ super(parentShell, 2, false);
+ mManager = Sdk.getCurrent().getLayoutDeviceManager();
+ }
+
+ @Override
+ protected int getShellStyle() {
+ return super.getShellStyle() | SWT.RESIZE;
+ }
+
+ @Override
+ protected void configureShell(Shell newShell) {
+ super.configureShell(newShell);
+ newShell.setText("Device Configurations");
+ }
+
+ @Override
+ public void createDialogContent(final Composite parent) {
+ GridData gd;
+ GridLayout gl;
+
+ Tree tree = new Tree(parent, SWT.SINGLE | SWT.FULL_SELECTION);
+ tree.setLayoutData(gd = new GridData(GridData.FILL_BOTH));
+ gd.widthHint = 700;
+
+ tree.setHeaderVisible(true);
+ tree.setLinesVisible(true);
+ TableHelper.createTreeColumn(tree, "Name", SWT.LEFT, 150, COL_NAME,
+ AdtPlugin.getDefault().getPreferenceStore());
+ TableHelper.createTreeColumn(tree, "Configuration", SWT.LEFT, 500, COL_CONFIG,
+ AdtPlugin.getDefault().getPreferenceStore());
+
+ mTreeViewer = new TreeViewer(tree);
+ mTreeViewer.setContentProvider(new DeviceContentProvider());
+ mTreeViewer.setLabelProvider(new DeviceLabelProvider());
+ mTreeViewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS);
+ mTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ setEnabled(getSelection());
+ }
+ });
+
+ Composite buttons = new Composite(parent, SWT.NONE);
+ buttons.setLayoutData(new GridData(GridData.FILL_VERTICAL));
+ buttons.setLayout(gl = new GridLayout());
+ gl.marginHeight = gl.marginWidth = 0;
+
+ mNewButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
+ mNewButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mNewButton.setText("New...");
+ mNewButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ DeviceSelection selection = getSelection();
+
+ ConfigEditDialog dlg = new ConfigEditDialog(parent.getShell(), null);
+ if (selection.device != null) {
+ dlg.setDeviceName(selection.device.getName());
+ dlg.setXDpi(selection.device.getXDpi());
+ dlg.setYDpi(selection.device.getYDpi());
+ }
+ if (selection.entry != null) {
+ dlg.setConfigName(selection.entry.getKey());
+ dlg.setConfig(selection.entry.getValue());
+ }
+
+ if (dlg.open() == Window.OK) {
+ String deviceName = dlg.getDeviceName();
+ String configName = dlg.getConfigName();
+ FolderConfiguration config = new FolderConfiguration();
+ dlg.getConfig(config);
+
+ // first if there was no original device, we create one.
+ // Because the new button is disabled when something else than "custom" is
+ // selected, we always add to the user devices without checking.
+ LayoutDevice d;
+ if (selection.device == null) {
+ // FIXME: this doesn't check if the device name is taken.
+ d = mManager.addUserDevice(deviceName, dlg.getXDpi(), dlg.getYDpi());
+ } else {
+ // search for it.
+ d = mManager.getUserLayoutDevice(deviceName);
+ }
+
+ if (d != null) {
+ // then if there was no config, we add it, otherwise we edit it
+ // (same method that adds/replace a config).
+ // FIXME this doesn't check if the name was already taken.
+ mManager.addUserConfiguration(d, configName, config);
+
+ mTreeViewer.refresh();
+ select(d, configName);
+ }
+ }
+ }
+ });
+
+ mEditButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
+ mEditButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mEditButton.setText("Edit...");
+ mEditButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ DeviceSelection selection = getSelection();
+ ConfigEditDialog dlg = new ConfigEditDialog(parent.getShell(), null);
+ dlg.setDeviceName(selection.device.getName());
+ dlg.setConfigName(selection.entry.getKey());
+ dlg.setConfig(selection.entry.getValue());
+
+ if (dlg.open() == Window.OK) {
+ String deviceName = dlg.getDeviceName();
+ String configName = dlg.getConfigName();
+ FolderConfiguration config = new FolderConfiguration();
+ dlg.getConfig(config);
+
+ // replace the device if needed.
+ // FIXME: this doesn't check if the replacement name doesn't exist already.
+ LayoutDevice d = mManager.replaceUserDevice(selection.device, deviceName,
+ dlg.getXDpi(), dlg.getYDpi());
+
+ // and add/replace the config
+ mManager.replaceUserConfiguration(d, selection.entry.getKey(), configName,
+ config);
+
+ mTreeViewer.refresh();
+ select(d, configName);
+ }
+ }
+ });
+
+ mCopyButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
+ mCopyButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mCopyButton.setText("Copy");
+ mCopyButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ DeviceSelection selection = getSelection();
+
+ // is the source a default/add-on device, or are we copying a full device?
+ // if so the target device is a new device.
+ LayoutDevice targetDevice = selection.device;
+ if (selection.type == DeviceType.DEFAULT || selection.type == DeviceType.ADDON ||
+ selection.entry == null) {
+ // create a new device
+ targetDevice = mManager.addUserDevice(
+ selection.device.getName() + " Copy", // new name
+ selection.device.getXDpi(),
+ selection.device.getYDpi());
+ }
+
+ String newConfigName = null; // name of the single new config. used for the select.
+
+ // are we copying the full device?
+ if (selection.entry == null) {
+ // get the config from the origin device
+ Map<String, FolderConfiguration> configs = selection.device.getConfigs();
+
+ // and copy them in the target device
+ for (Entry<String, FolderConfiguration> entry : configs.entrySet()) {
+ // we need to make a copy of the config object, or it could be modified
+ // in default/addon by editing the version in the new device.
+ FolderConfiguration copy = new FolderConfiguration();
+ copy.set(entry.getValue());
+
+ // the name can stay the same since we are copying a full device
+ // and the target device has its own new name.
+ mManager.addUserConfiguration(targetDevice, entry.getKey(), copy);
+ }
+ } else {
+ // only copy the config. target device is not the same as the selection, don't
+ // change the config name as we already changed the name of the device.
+ newConfigName = (selection.device != targetDevice) ?
+ selection.entry.getKey() : selection.entry.getKey() + " Copy";
+
+ // copy of the config
+ FolderConfiguration copy = new FolderConfiguration();
+ copy.set(selection.entry.getValue());
+
+ // and create the config
+ mManager.addUserConfiguration(targetDevice, newConfigName, copy);
+ }
+
+ mTreeViewer.refresh();
+
+ select(targetDevice, newConfigName);
+ }
+ });
+
+ mDeleteButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
+ mDeleteButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ mDeleteButton.setText("Delete");
+ mDeleteButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ DeviceSelection selection = getSelection();
+
+ if (selection.entry != null) {
+ mManager.removeUserConfiguration(selection.device, selection.entry.getKey());
+ } else if (selection.device != null) {
+ mManager.removeUserDevice(selection.device);
+ }
+
+ mTreeViewer.refresh();
+
+ // either select the device (if we removed a entry, or the top custom node if
+ // we removed a device)
+ select(selection.entry != null ? selection.device : null, null);
+ }
+ });
+
+ Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
+ separator.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
+ gd.horizontalSpan = 2;
+
+ mTreeViewer.setInput(mManager);
+ setEnabled(null); // no selection at the start
+ }
+
+ @Override
+ protected void createButtonsForButtonBar(Composite parent) {
+ // we only want an OK button.
+ createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
+ }
+
+ /**
+ * Returns a {@link DeviceSelection} object representing the selected path in the
+ * {@link TreeViewer}
+ */
+ private DeviceSelection getSelection() {
+ // get the selection paths
+ TreeSelection selection = (TreeSelection)mTreeViewer.getSelection();
+ TreePath[] paths =selection.getPaths();
+
+ if (paths.length == 0) {
+ return null;
+ }
+
+ TreePath pathSelection = paths[0];
+
+ DeviceType type = (DeviceType)pathSelection.getFirstSegment();
+ LayoutDevice device = null;
+ Entry<String, FolderConfiguration> entry = null;
+ switch (pathSelection.getSegmentCount()) {
+ case 2: // layout device is selected
+ device = (LayoutDevice)pathSelection.getLastSegment();
+ break;
+ case 3: // config is selected
+ device = (LayoutDevice)pathSelection.getSegment(1);
+ entry = (Entry<String, FolderConfiguration>)pathSelection.getLastSegment();
+ }
+
+ return new DeviceSelection(type, device, entry);
+ }
+
+ /**
+ * Enables/disables the action button based on the {@link DeviceSelection}.
+ * @param selection the selection
+ */
+ protected void setEnabled(DeviceSelection selection) {
+ if (selection == null) {
+ mNewButton.setEnabled(false);
+ mEditButton.setEnabled(false);
+ mCopyButton.setEnabled(false);
+ mDeleteButton.setEnabled(false);
+ } else {
+ switch (selection.type) {
+ case DEFAULT:
+ case ADDON:
+ // only allow copy if device is not null
+ mNewButton.setEnabled(false);
+ mEditButton.setEnabled(false);
+ mDeleteButton.setEnabled(false);
+ mCopyButton.setEnabled(selection.device != null);
+ break;
+ case CUSTOM:
+ mNewButton.setEnabled(true); // always true to create new devices.
+ mEditButton.setEnabled(selection.entry != null); // only edit config for now
+
+ boolean enabled = selection.device != null; // need at least selected device
+ mDeleteButton.setEnabled(enabled); // for delete and copy buttons
+ mCopyButton.setEnabled(enabled);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Selects a device and optionally a config. Because this is meant to show newly created/edited
+ * device/config, it'll only do so for {@link DeviceType#CUSTOM} devices.
+ * @param device the device to select
+ * @param configName the config to select (optional)
+ */
+ private void select(LayoutDevice device, String configName) {
+ Object[] path;
+ if (device == null) {
+ // select the "custom" node
+ path = new Object[] { DeviceType.CUSTOM };
+ } else if (configName == null) {
+ // this is the easy case. no config to select
+ path = new Object[] { DeviceType.CUSTOM, device };
+ } else {
+ // this is more complex. we have the configName, but the tree contains Entry<?,?>
+ // Look for the entry.
+ Entry<?, ?> match = null;
+ for (Entry<?, ?> entry : device.getConfigs().entrySet()) {
+ if (entry.getKey().equals(configName)) {
+ match = entry;
+ break;
+ }
+ }
+
+ if (match != null) {
+ path = new Object[] { DeviceType.CUSTOM, device, match };
+ } else {
+ path = new Object[] { DeviceType.CUSTOM, device };
+ }
+ }
+
+ mTreeViewer.setSelection(new TreeSelection(new TreePath(path)), true /*reveal*/);
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
index 598b11d..8c37cc6 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
@@ -29,7 +29,8 @@
import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier.Density;
import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier.ScreenOrientation;
import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
-import com.android.ide.eclipse.adt.internal.sdk.DeviceConfiguration;
+import com.android.ide.eclipse.adt.internal.sdk.LayoutDevice;
+import com.android.ide.eclipse.adt.internal.sdk.LayoutDeviceManager;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.LanguageRegionVerifier;
import com.android.layoutlib.api.IResourceValue;
@@ -77,7 +78,7 @@
/** The {@link FolderConfiguration} representing the state of the UI controls */
private final FolderConfiguration mCurrentConfig = new FolderConfiguration();
- private List<DeviceConfiguration> mDevices;
+ private List<LayoutDevice> mDevices;
private final ArrayList<ResourceQualifier[] > mLocaleList =
new ArrayList<ResourceQualifier[]>();
@@ -86,7 +87,7 @@
private boolean mClipping = true;
- private DeviceConfiguration mCurrentDevice;
+ private LayoutDevice mCurrentDevice;
/**
* Interface implemented by the part which owns a {@link ConfigurationComposite}.
@@ -109,9 +110,6 @@
public ConfigurationComposite(IConfigListener listener, Composite parent, int style) {
super(parent, style);
mListener = listener;
- if (Sdk.getCurrent() != null) {
- mDevices = Sdk.getCurrent().getLayoutDevices();
- }
GridLayout gl;
GridData gd;
@@ -512,15 +510,25 @@
}
/**
- * Reloads the list of {@link DeviceConfiguration} from the {@link Sdk}.
+ * Reloads the list of {@link LayoutDevice} from the {@link Sdk}.
* @param notifyListener
*/
public void reloadDevices(boolean notifyListener) {
- mDevices = Sdk.getCurrent().getLayoutDevices();
+ loadDevices();
initUiWithDevices();
onDeviceChange(notifyListener);
}
+ private void loadDevices() {
+ mDevices = null;
+
+ Sdk sdk = Sdk.getCurrent();
+ if (sdk != null) {
+ LayoutDeviceManager manager = sdk.getLayoutDeviceManager();
+ mDevices = manager.getCombinedList();
+ }
+ }
+
/**
* Init the UI with the list of Devices.
*/
@@ -531,7 +539,7 @@
// fill with the devices
if (mDevices != null) {
- for (DeviceConfiguration device : mDevices) {
+ for (LayoutDevice device : mDevices) {
mDeviceList.add(device.getName());
}
mDeviceList.select(0);
@@ -548,6 +556,9 @@
}
}
}
+
+ // add the custom item
+ mDeviceList.add("Custom...");
}
/**
@@ -575,6 +586,31 @@
int deviceIndex = mDeviceList.getSelectionIndex();
if (deviceIndex != -1) {
+ // check if the user is ask for the custom item
+ if (deviceIndex == mDeviceList.getItemCount() - 1) {
+ ConfigManagerDialog dialog = new ConfigManagerDialog(getShell());
+ dialog.open();
+
+ // reload the combo with the new content.
+ loadDevices();
+ initUiWithDevices();
+
+ // at this point we need to reset the combo to something (hopefully) valid.
+ // look for the previous selected device
+ int index = mDevices.indexOf(mCurrentDevice);
+ if (index != -1) {
+ mDeviceList.select(index);
+ } else {
+ // we should at least have one built-in device, so we select it
+ mDeviceList.select(0);
+ }
+
+ // force a redraw
+ onDeviceChange(true /*recomputeLayout*/);
+
+ return;
+ }
+
mCurrentDevice = mDevices.get(deviceIndex);
} else {
mCurrentDevice = null;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutCreatorDialog.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LayoutCreatorDialog.java
similarity index 86%
rename from tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutCreatorDialog.java
rename to tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LayoutCreatorDialog.java
index a55f1d0..eb8c5da 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutCreatorDialog.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/LayoutCreatorDialog.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.ide.eclipse.adt.internal.editors.layout;
+package com.android.ide.eclipse.adt.internal.editors.layout.configuration;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
@@ -23,21 +23,21 @@
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector;
import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.ConfigurationState;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdkuilib.ui.GridDialog;
+import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
-import org.eclipse.jface.dialogs.TrayDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
/**
* Dialog to choose a non existing {@link FolderConfiguration}.
*/
-class LayoutCreatorDialog extends TrayDialog {
+public final class LayoutCreatorDialog extends GridDialog {
private ConfigurationSelector mSelector;
private Composite mStatusComposite;
@@ -53,9 +53,9 @@
* @param parentShell the parent {@link Shell}.
* @param config The starting configuration.
*/
- LayoutCreatorDialog(Shell parentShell, String fileName, IAndroidTarget target,
+ public LayoutCreatorDialog(Shell parentShell, String fileName, IAndroidTarget target,
FolderConfiguration config) {
- super(parentShell);
+ super(parentShell, 1, false);
mFileName = fileName;
mTarget = target;
@@ -65,15 +65,11 @@
}
@Override
- protected Control createDialogArea(Composite parent) {
- Composite top = new Composite(parent, SWT.NONE);
- top.setLayoutData(new GridData());
- top.setLayout(new GridLayout(1, false));
-
- new Label(top, SWT.NONE).setText(
+ public void createDialogContent(Composite parent) {
+ new Label(parent, SWT.NONE).setText(
String.format("Configuration for the alternate version of %1$s", mFileName));
- mSelector = new ConfigurationSelector(top);
+ mSelector = new ConfigurationSelector(parent);
mSelector.setConfiguration(mConfig);
// parent's layout is a GridLayout as specified in the javadoc.
@@ -117,7 +113,7 @@
}
});
- mStatusComposite = new Composite(top, SWT.NONE);
+ mStatusComposite = new Composite(parent, SWT.NONE);
mStatusComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
GridLayout gl = new GridLayout(2, false);
mStatusComposite.setLayout(gl);
@@ -127,8 +123,6 @@
mStatusLabel = new Label(mStatusComposite, SWT.NONE);
mStatusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
resetStatus();
-
- return top;
}
public void getConfiguration(FolderConfiguration config) {
@@ -139,7 +133,9 @@
* resets the status label to show the file that will be created.
*/
private void resetStatus() {
- mStatusLabel.setText(String.format("New File: res/%1$s/%2$s",
- mConfig.getFolderName(ResourceFolderType.LAYOUT, mTarget), mFileName));
+ String displayString = Dialog.shortenText(String.format("New File: res/%1$s/%2$s",
+ mConfig.getFolderName(ResourceFolderType.LAYOUT, mTarget), mFileName),
+ mStatusLabel);
+ mStatusLabel.setText(displayString);
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java
index ae86f48..f1ac8cf 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/configurations/FolderConfiguration.java
@@ -60,8 +60,10 @@
* @param config
*/
public void set(FolderConfiguration config) {
- for (int i = 0 ; i < INDEX_COUNT ; i++) {
- mQualifiers[i] = config.mQualifiers[i];
+ if (config != null) {
+ for (int i = 0 ; i < INDEX_COUNT ; i++) {
+ mQualifiers[i] = config.mQualifiers[i];
+ }
}
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java
index 0d93a0c..abd7cf8 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetData.java
@@ -86,7 +86,6 @@
private LayoutBridge mLayoutBridge;
private boolean mLayoutBridgeInit = false;
- private DeviceConfiguration[] mDevices;
AndroidTargetData(IAndroidTarget androidTarget) {
mTarget = androidTarget;
@@ -114,7 +113,6 @@
String[] intentCategoryValues,
String[] platformLibraries,
IOptionalLibrary[] optionalLibraries,
- DeviceConfiguration[] devices,
ProjectResources resources,
LayoutBridge layoutBridge) {
@@ -124,7 +122,6 @@
mMenuDescriptors = menuDescriptors;
mXmlDescriptors = xmlDescriptors;
mEnumValueMap = enumValueMap;
- mDevices = devices;
mFrameworkResources = resources;
mLayoutBridge = layoutBridge;
@@ -276,10 +273,6 @@
return mLayoutBridge;
}
- public DeviceConfiguration[] getDevices() {
- return mDevices;
- }
-
/**
* Sets the permission values
* @param permissionValues the list of permissions
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java
index 9e14579..a499137 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/AndroidTargetParser.java
@@ -254,9 +254,6 @@
LayoutBridge layoutBridge = loadLayoutBridge();
progress.worked(1);
- // get the devices
- DeviceConfiguration[] devices = getDevices();
-
// and finally create the PlatformData with all that we loaded.
targetData.setExtraData(frameworkRepository,
manifestDescriptors,
@@ -271,7 +268,6 @@
categories.toArray(new String[categories.size()]),
mAndroidTarget.getPlatformLibraries(),
mAndroidTarget.getOptionalLibraries(),
- devices,
resources,
layoutBridge);
@@ -705,10 +701,4 @@
return layoutBridge;
}
-
- private DeviceConfiguration[] getDevices() {
- // TODO: load this from the target.
- return null;
- }
-
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/DeviceConfiguration.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/DeviceConfiguration.java
deleted file mode 100644
index eb1ddc2..0000000
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/DeviceConfiguration.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.internal.sdk;
-
-import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-public class DeviceConfiguration {
-
- private final String mName;
-
- private Map<String, FolderConfiguration> mMap =
- new HashMap<String, FolderConfiguration>();
- private float mXDpi = Float.NaN;
- private float mYDpi = Float.NaN;
-
- DeviceConfiguration(String name) {
- mName = name;
- }
-
- void addConfig(String name, FolderConfiguration config) {
- mMap.put(name, config);
- }
-
- void seal() {
- mMap = Collections.unmodifiableMap(mMap);
- }
-
- void setXDpi(float xdpi) {
- mXDpi = xdpi;
- }
-
- void setYDpi(float ydpi) {
- mYDpi = ydpi;
- }
-
- public String getName() {
- return mName;
- }
-
- public Map<String, FolderConfiguration> getConfigs() {
- return mMap;
- }
-
- /**
- * Returns the dpi of the Device screen in X.
- * @return the dpi of screen or {@link Float#NaN} if it's not set.
- */
- public float getXDpi() {
- return mXDpi;
- }
-
- /**
- * Returns the dpi of the Device screen in Y.
- * @return the dpi of screen or {@link Float#NaN} if it's not set.
- */
- public float getYDpi() {
- return mYDpi;
- }
- }
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDevice.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDevice.java
new file mode 100644
index 0000000..89cac5b
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDevice.java
@@ -0,0 +1,116 @@
+/*
+ * 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.internal.sdk;
+
+import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Class representing a layout device.
+ *
+ * A Layout device is a collection of {@link FolderConfiguration} that can be used to render Android
+ * layout files.
+ *
+ * It also contains a single xdpi/ydpi that is independent of the {@link FolderConfiguration}.
+ *
+ * If the device is meant to represent a true device, then most of the FolderConfigurations' content
+ * should be identical, with only a few qualifiers (orientation, keyboard state) that would differ.
+ * However it is simpler to reuse the FolderConfiguration class (with the non changing qualifiers
+ * duplicated in each configuration) as it's what's being used by the rendering library.
+ *
+ * To create, edit and delete LayoutDevice objects, see {@link LayoutDeviceManager}.
+ * The class is not technically immutable but behaves as such outside of its package.
+ */
+public class LayoutDevice {
+
+ private final String mName;
+
+ /** editable map of the config */
+ private Map<String, FolderConfiguration> mEditMap = new HashMap<String, FolderConfiguration>();
+ /** unmodifiable map returned by {@link #getConfigs()}. */
+ private Map<String, FolderConfiguration> mMap;
+ private float mXDpi = Float.NaN;
+ private float mYDpi = Float.NaN;
+
+ LayoutDevice(String name) {
+ mName = name;
+ }
+
+ void addConfig(String name, FolderConfiguration config) {
+ mEditMap.put(name, config);
+ _seal();
+ }
+
+ void addConfigs(Map<String, FolderConfiguration> configs) {
+ mEditMap.putAll(configs);
+ _seal();
+ }
+
+ void removeConfig(String name) {
+ mEditMap.remove(name);
+ _seal();
+ }
+
+ /**
+ * Adds config to the LayoutDevice. This is to be used to add plenty of configurations.
+ * It must be followed by {@link #_seal()}.
+ * @param name the name of the config
+ * @param config the config.
+ */
+ void _addConfig(String name, FolderConfiguration config) {
+ mEditMap.put(name, config);
+ }
+
+ void _seal() {
+ mMap = Collections.unmodifiableMap(mEditMap);
+ }
+
+ void setXDpi(float xdpi) {
+ mXDpi = xdpi;
+ }
+
+ void setYDpi(float ydpi) {
+ mYDpi = ydpi;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public Map<String, FolderConfiguration> getConfigs() {
+ return mMap;
+ }
+
+ /**
+ * Returns the dpi of the Device screen in X.
+ * @return the dpi of screen or {@link Float#NaN} if it's not set.
+ */
+ public float getXDpi() {
+ return mXDpi;
+ }
+
+ /**
+ * Returns the dpi of the Device screen in Y.
+ * @return the dpi of screen or {@link Float#NaN} if it's not set.
+ */
+ public float getYDpi() {
+ return mYDpi;
+ }
+ }
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceHandler.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceHandler.java
index 71d7f18..36932bf 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceHandler.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceHandler.java
@@ -58,16 +58,16 @@
* on the endElement, by using the content found in characters().
*/
- private List<DeviceConfiguration> mDevices = new ArrayList<DeviceConfiguration>();
+ private List<LayoutDevice> mDevices = new ArrayList<LayoutDevice>();
- private DeviceConfiguration mCurrentDevice;
+ private LayoutDevice mCurrentDevice;
private FolderConfiguration mDefaultConfig;
private FolderConfiguration mCurrentConfig;
private final StringBuilder mStringAccumulator = new StringBuilder();
private String mSize1, mSize2;
- public List<DeviceConfiguration> getDevices() {
+ public List<LayoutDevice> getDevices() {
return mDevices;
}
@@ -79,7 +79,7 @@
String deviceName = attributes.getValue("", LayoutConfigsXsd.ATTR_NAME);
// create a device and add it to the list
- mCurrentDevice = new DeviceConfiguration(deviceName);
+ mCurrentDevice = new LayoutDevice(deviceName);
mDevices.add(mCurrentDevice);
} else if (LayoutConfigsXsd.NODE_DEFAULT.equals(localName)) {
// create a new default config
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceManager.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceManager.java
new file mode 100644
index 0000000..03daf40
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/LayoutDeviceManager.java
@@ -0,0 +1,310 @@
+/*
+ * 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.internal.sdk;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
+import com.android.sdklib.SdkConstants;
+
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Validator;
+
+/**
+ * Manages the layout devices.
+ * They can come from 3 sources: built-in, add-ons, user.
+ */
+public class LayoutDeviceManager {
+
+ /**
+ * A SAX error handler that captures the errors and warnings.
+ * This allows us to capture *all* errors and just not get an exception on the first one.
+ */
+ private static class CaptureErrorHandler implements ErrorHandler {
+
+ private final String mSourceLocation;
+
+ private boolean mFoundError = false;
+
+ CaptureErrorHandler(String sourceLocation) {
+ mSourceLocation = sourceLocation;
+ }
+
+ public boolean foundError() {
+ return mFoundError;
+ }
+
+ /**
+ * @throws SAXException
+ */
+ public void error(SAXParseException ex) throws SAXException {
+ mFoundError = true;
+ AdtPlugin.log(ex, "Error validating %1$s", mSourceLocation);
+ }
+
+ /**
+ * @throws SAXException
+ */
+ public void fatalError(SAXParseException ex) throws SAXException {
+ mFoundError = true;
+ AdtPlugin.log(ex, "Error validating %1$s", mSourceLocation);
+ }
+
+ /**
+ * @throws SAXException
+ */
+ public void warning(SAXParseException ex) throws SAXException {
+ // ignore those for now.
+ }
+ }
+
+ private final SAXParserFactory mParserFactory;
+
+ private List<LayoutDevice> mDefaultLayoutDevices =
+ new ArrayList<LayoutDevice>();
+ private List<LayoutDevice> mAddOnLayoutDevices =
+ new ArrayList<LayoutDevice>();
+ private final List<LayoutDevice> mUserLayoutDevices =
+ new ArrayList<LayoutDevice>();
+ private List<LayoutDevice> mLayoutDevices;
+
+ LayoutDeviceManager() {
+ mParserFactory = SAXParserFactory.newInstance();
+ mParserFactory.setNamespaceAware(true);
+ }
+
+ public List<LayoutDevice> getCombinedList() {
+ return mLayoutDevices;
+ }
+
+ public List<LayoutDevice> getDefaultLayoutDevices() {
+ return mDefaultLayoutDevices;
+ }
+
+ public List<LayoutDevice> getAddOnLayoutDevice() {
+ return mAddOnLayoutDevices;
+ }
+
+ public List<LayoutDevice> getUserLayoutDevices() {
+ return mUserLayoutDevices;
+ }
+
+ public LayoutDevice getUserLayoutDevice(String name) {
+ for (LayoutDevice d : mUserLayoutDevices) {
+ if (d.getName().equals(name)) {
+ return d;
+ }
+ }
+
+ return null;
+ }
+
+ public LayoutDevice addUserDevice(String name, float xdpi, float ydpi) {
+ LayoutDevice d = new LayoutDevice(name);
+ d.setXDpi(xdpi);
+ d.setYDpi(ydpi);
+ mUserLayoutDevices.add(d);
+ combineLayoutDevices();
+
+ return d;
+ }
+
+ public void removeUserDevice(LayoutDevice device) {
+ if (mUserLayoutDevices.remove(device)) {
+ combineLayoutDevices();
+ }
+ }
+
+ /**
+ * Replaces a device with a new one with new name and/or x/y dpi, and return the new device.
+ * If the name and dpi values are identical the given device is returned an nothing is done
+ * @param device the {@link LayoutDevice} to replace
+ * @param newName the new name.
+ * @param newXDpi the new X dpi value
+ * @param newYDpi the new Y dpi value.
+ * @return the new LayoutDevice
+ */
+ public LayoutDevice replaceUserDevice(LayoutDevice device, String newName,
+ float newXDpi, float newYDpi) {
+ if (device.getName().equals(newName) && device.getXDpi() == newXDpi &&
+ device.getYDpi() == newYDpi) {
+ return device;
+ }
+
+ // else create a new device
+ LayoutDevice newDevice = new LayoutDevice(newName);
+ newDevice.setXDpi(newXDpi);
+ newDevice.setYDpi(newYDpi);
+
+ // and get the Folderconfiguration
+ Map<String, FolderConfiguration> configs = device.getConfigs();
+ newDevice.addConfigs(configs);
+
+ // replace the old device with the new
+ mUserLayoutDevices.remove(device);
+ mUserLayoutDevices.add(newDevice);
+ combineLayoutDevices();
+
+ return newDevice;
+ }
+
+
+ /**
+ * Adds or replaces a configuration in a given {@link LayoutDevice}.
+ * @param device the device to modify
+ * @param configName the configuration name to add or replace
+ * @param config the configuration to set
+ */
+ public void addUserConfiguration(LayoutDevice device, String configName,
+ FolderConfiguration config) {
+ // check that the device does belong to the user list.
+ // the main goal is to make sure that this does not belong to the default/addon list.
+ if (mUserLayoutDevices.contains(device)) {
+ device.addConfig(configName, config);
+ }
+ }
+
+ /**
+ * Replaces a configuration in a given {@link LayoutDevice}.
+ * @param device the device to modify
+ * @param oldConfigName the name of the config to replace. If null, the new config is simply
+ * added.
+ * @param newConfigName the configuration name to add or replace
+ * @param config the configuration to set
+ */
+ public void replaceUserConfiguration(LayoutDevice device, String oldConfigName,
+ String newConfigName, FolderConfiguration config) {
+ // check that the device does belong to the user list.
+ // the main goal is to make sure that this does not belong to the default/addon list.
+ if (mUserLayoutDevices.contains(device)) {
+ // if the old and new config name are different, remove the old one
+ if (oldConfigName != null && oldConfigName.equals(newConfigName) == false) {
+ device.removeConfig(oldConfigName);
+ }
+
+ // and then add the new one
+ device.addConfig(newConfigName, config);
+ }
+ }
+
+ /**
+ * Removes a configuration from a given user {@link LayoutDevice}
+ * @param device the device to modify
+ * @param configName the name of the config to remove
+ */
+ public void removeUserConfiguration(LayoutDevice device, String configName) {
+ // check that the device does belong to the user list.
+ // the main goal is to make sure that this does not belong to the default/addon list.
+ if (mUserLayoutDevices.contains(device)) {
+ device.removeConfig(configName);
+ }
+ }
+
+ void load(String sdkOsLocation) {
+ // load the default devices
+ loadDefaultLayoutDevices(sdkOsLocation);
+
+ // load the user devices;
+ }
+
+ void parseAddOnLayoutDevice(File deviceXml) {
+ parseLayoutDevices(deviceXml, mAddOnLayoutDevices);
+ }
+
+ void sealAddonLayoutDevices() {
+ mAddOnLayoutDevices = Collections.unmodifiableList(mAddOnLayoutDevices);
+
+ combineLayoutDevices();
+ }
+
+ /**
+ * Does the actual parsing of a devices.xml file.
+ */
+ private void parseLayoutDevices(File deviceXml, List<LayoutDevice> list) {
+ // first we validate the XML
+ try {
+ Source source = new StreamSource(new FileReader(deviceXml));
+
+ CaptureErrorHandler errorHandler = new CaptureErrorHandler(deviceXml.getAbsolutePath());
+
+ Validator validator = LayoutConfigsXsd.getValidator(errorHandler);
+ validator.validate(source);
+
+ if (errorHandler.foundError() == false) {
+ // do the actual parsing
+ LayoutDeviceHandler handler = new LayoutDeviceHandler();
+
+ SAXParser parser = mParserFactory.newSAXParser();
+ parser.parse(new InputSource(new FileInputStream(deviceXml)), handler);
+
+ // get the parsed devices
+ list.addAll(handler.getDevices());
+ }
+ } catch (SAXException e) {
+ AdtPlugin.log(e, "Error parsing %1$s", deviceXml.getAbsoluteFile());
+ } catch (FileNotFoundException e) {
+ // this shouldn't happen as we check above.
+ } catch (IOException e) {
+ AdtPlugin.log(e, "Error reading %1$s", deviceXml.getAbsoluteFile());
+ } catch (ParserConfigurationException e) {
+ AdtPlugin.log(e, "Error parsing %1$s", deviceXml.getAbsoluteFile());
+ }
+ }
+
+ /**
+ * Creates some built-it layout devices.
+ */
+ private void loadDefaultLayoutDevices(String sdkOsLocation) {
+ ArrayList<LayoutDevice> list = new ArrayList<LayoutDevice>();
+ File toolsFolder = new File(sdkOsLocation, SdkConstants.OS_SDK_TOOLS_LIB_FOLDER);
+ if (toolsFolder.isDirectory()) {
+ File deviceXml = new File(toolsFolder, SdkConstants.FN_DEVICES_XML);
+ if (deviceXml.isFile()) {
+ parseLayoutDevices(deviceXml, list);
+ }
+ }
+ mDefaultLayoutDevices = Collections.unmodifiableList(list);
+ }
+
+ private void combineLayoutDevices() {
+ ArrayList<LayoutDevice> list = new ArrayList<LayoutDevice>();
+ list.addAll(mDefaultLayoutDevices);
+ list.addAll(mAddOnLayoutDevices);
+ list.addAll(mUserLayoutDevices);
+
+ mLayoutDevices = Collections.unmodifiableList(list);
+ }
+
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
index 9fd90fe..6a8bd5b 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java
@@ -41,31 +41,15 @@
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-import javax.xml.transform.Source;
-import javax.xml.transform.stream.StreamSource;
-import javax.xml.validation.Validator;
-
/**
* Central point to load, manipulate and deal with the Android SDK. Only one SDK can be used
* at the same time.
@@ -89,7 +73,7 @@
new HashMap<IProject, ApkSettings>();
private final String mDocBaseUrl;
- private List<DeviceConfiguration> mLayoutDevices = new ArrayList<DeviceConfiguration>();
+ private final LayoutDeviceManager mLayoutDeviceManager = new LayoutDeviceManager();
/**
* Classes implementing this interface will receive notification when targets are changed.
@@ -439,6 +423,10 @@
}
}
+ public LayoutDeviceManager getLayoutDeviceManager() {
+ return mLayoutDeviceManager;
+ }
+
private Sdk(SdkManager manager, AvdManager avdManager) {
mManager = manager;
mAvdManager = avdManager;
@@ -451,8 +439,10 @@
mDocBaseUrl = getDocumentationBaseUrl(mManager.getLocation() +
SdkConstants.OS_SDK_DOCS_FOLDER);
- // load the built-in layout devices
- loadDefaultLayoutDevices();
+ // load the built-in and user layout devices
+ mLayoutDeviceManager.load(mManager.getLocation());
+ // and the ones from the add-on
+ loadLayoutDevices();
}
/**
@@ -505,6 +495,24 @@
return null;
}
+ /**
+ * Parses the SDK add-ons to look for files called {@link SdkConstants#FN_DEVICES_XML} to
+ * load {@link LayoutDevice} from them.
+ */
+ private void loadLayoutDevices() {
+ IAndroidTarget[] targets = mManager.getTargets();
+ for (IAndroidTarget target : targets) {
+ if (target.isPlatform() == false) {
+ File deviceXml = new File(target.getLocation(), SdkConstants.FN_DEVICES_XML);
+ if (deviceXml.isFile()) {
+ mLayoutDeviceManager.parseAddOnLayoutDevice(deviceXml);
+ }
+ }
+ }
+
+ mLayoutDeviceManager.sealAddonLayoutDevices();
+ }
+
public void projectClosed(IProject project) {
// get the target project
synchronized (AdtPlugin.getDefault().getSdkLockObject()) {
@@ -539,128 +547,5 @@
// ignore this. The project will be added to the map the first time the target needs
// to be resolved.
}
-
-
- // ---------- Device Configuration methods ----------
-
- /**
- * A SAX error handler that captures the errors and warnings.
- * This allows us to capture *all* errors and just not get an exception on the first one.
- */
- private static class CaptureErrorHandler implements ErrorHandler {
-
- private final String mSourceLocation;
-
- private boolean mFoundError = false;
-
- CaptureErrorHandler(String sourceLocation) {
- mSourceLocation = sourceLocation;
- }
-
- public boolean foundError() {
- return mFoundError;
- }
-
- /**
- * @throws SAXException
- */
- public void error(SAXParseException ex) throws SAXException {
- mFoundError = true;
- AdtPlugin.log(ex, "Error validating %1$s", mSourceLocation);
- }
-
- /**
- * @throws SAXException
- */
- public void fatalError(SAXParseException ex) throws SAXException {
- mFoundError = true;
- AdtPlugin.log(ex, "Error validating %1$s", mSourceLocation);
- }
-
- /**
- * @throws SAXException
- */
- public void warning(SAXParseException ex) throws SAXException {
- // ignore those for now.
- }
- }
-
- /**
- * Returns the list of {@link DeviceConfiguration} found in the SDK.
- */
- public List<DeviceConfiguration> getLayoutDevices() {
- return mLayoutDevices;
- }
-
- /**
- * Parses the SDK add-ons to look for files called {@link SdkConstants#FN_DEVICES_XML} to
- * load {@link DeviceConfiguration} from them.
- */
- public void parseAddOnLayoutDevices() {
- SAXParserFactory parserFactory = SAXParserFactory.newInstance();
- parserFactory.setNamespaceAware(true);
-
- IAndroidTarget[] targets = mManager.getTargets();
- for (IAndroidTarget target : targets) {
- if (target.isPlatform() == false) {
- File deviceXml = new File(target.getLocation(), SdkConstants.FN_DEVICES_XML);
- if (deviceXml.isFile()) {
- parseLayoutDevices(parserFactory, deviceXml);
- }
- }
- }
-
- mLayoutDevices = Collections.unmodifiableList(mLayoutDevices);
- }
-
- /**
- * Does the actual parsing of a devices.xml file.
- */
- private void parseLayoutDevices(SAXParserFactory parserFactory, File deviceXml) {
- // first we validate the XML
- try {
- Source source = new StreamSource(new FileReader(deviceXml));
-
- CaptureErrorHandler errorHandler = new CaptureErrorHandler(deviceXml.getAbsolutePath());
-
- Validator validator = LayoutConfigsXsd.getValidator(errorHandler);
- validator.validate(source);
-
- if (errorHandler.foundError() == false) {
- // do the actual parsing
- LayoutDeviceHandler handler = new LayoutDeviceHandler();
-
- SAXParser parser = parserFactory.newSAXParser();
- parser.parse(new InputSource(new FileInputStream(deviceXml)), handler);
-
- // get the parsed devices
- mLayoutDevices.addAll(handler.getDevices());
- }
- } catch (SAXException e) {
- AdtPlugin.log(e, "Error parsing %1$s", deviceXml.getAbsoluteFile());
- } catch (FileNotFoundException e) {
- // this shouldn't happen as we check above.
- } catch (IOException e) {
- AdtPlugin.log(e, "Error reading %1$s", deviceXml.getAbsoluteFile());
- } catch (ParserConfigurationException e) {
- AdtPlugin.log(e, "Error parsing %1$s", deviceXml.getAbsoluteFile());
- }
- }
-
- /**
- * Creates some built-it layout devices.
- */
- private void loadDefaultLayoutDevices() {
- SAXParserFactory parserFactory = SAXParserFactory.newInstance();
- parserFactory.setNamespaceAware(true);
-
- File toolsFolder = new File(mManager.getLocation(), SdkConstants.OS_SDK_TOOLS_LIB_FOLDER);
- if (toolsFolder.isDirectory()) {
- File deviceXml = new File(toolsFolder, SdkConstants.FN_DEVICES_XML);
- if (deviceXml.isFile()) {
- parseLayoutDevices(parserFactory, deviceXml);
- }
- }
- }
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ConfigurationSelector.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ConfigurationSelector.java
index 358b6fd..9e3acec 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ConfigurationSelector.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ConfigurationSelector.java
@@ -77,6 +77,7 @@
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
+import java.util.ArrayList;
import java.util.HashMap;
/**
@@ -109,6 +110,8 @@
private final HashMap<Class<? extends ResourceQualifier>, QualifierEditBase> mUiMap =
new HashMap<Class<? extends ResourceQualifier>, QualifierEditBase>();
private Composite mQualifierEditParent;
+ private boolean mDeviceMode = false;
+ private IQualifierFilter mQualifierFilter;
/**
* Basic of {@link VerifyListener} to only accept digits.
@@ -184,6 +187,17 @@
OK, INVALID_CONFIG, REGION_WITHOUT_LANGUAGE;
}
+ /**
+ * A filter for {@link ResourceQualifier}.
+ * @see ConfigurationSelector#setQualifierFilter(IQualifierFilter)
+ */
+ public interface IQualifierFilter {
+ /**
+ * Returns true of the qualifier is accepted.
+ */
+ boolean accept(ResourceQualifier qualifier);
+ }
+
public ConfigurationSelector(Composite parent) {
super(parent, SWT.NONE);
@@ -375,6 +389,28 @@
}
/**
+ * Sets the device mode. If <code>true</code> then the configuration selector only
+ * allows to create configuration that are valid on a device (as opposed to resource
+ * configuration).
+ * For instance {@link Density#NODPI} is a valid qualifier for a resource configuration but
+ * this is not valid on a device.
+ * Default is false.
+ * @param deviceMode the device mode.
+ */
+ public void setDeviceMode(boolean deviceMode) {
+ mDeviceMode = deviceMode;
+ }
+
+ /**
+ * Sets a {@link IQualifierFilter}. If non null, this will restrict the qualifiers that
+ * can be chosen.
+ * @param filter the filter to set.
+ */
+ public void setQualifierFilter(IQualifierFilter filter) {
+ mQualifierFilter = filter;
+ }
+
+ /**
* Sets a listener to be notified when the configuration changes.
* @param listener A {@link Runnable} whose <code>run()</code> method is called when the
* configuration is changed. The method is called from the UI thread.
@@ -492,7 +528,7 @@
/**
* Content provider around a {@link FolderConfiguration}.
*/
- private static class QualifierContentProvider implements IStructuredContentProvider {
+ private class QualifierContentProvider implements IStructuredContentProvider {
private FolderConfiguration mInput;
@@ -504,7 +540,20 @@
}
public Object[] getElements(Object inputElement) {
- return mInput.getQualifiers();
+ // default easy case
+ if (mQualifierFilter == null) {
+ return mInput.getQualifiers();
+ }
+
+ // in this case we have to compute the list
+ ArrayList<ResourceQualifier> list = new ArrayList<ResourceQualifier>();
+ for (ResourceQualifier qual : mInput.getQualifiers()) {
+ if (mQualifierFilter.accept(qual)) {
+ list.add(qual);
+ }
+ }
+
+ return list.toArray();
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
@@ -1043,7 +1092,9 @@
mDensity = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
Density[] soValues = Density.values();
for (Density value : soValues) {
- mDensity.add(value.getDisplayValue());
+ if (mDeviceMode == false || value != Density.NODPI) {
+ mDensity.add(value.getDisplayValue());
+ }
}
mDensity.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
@@ -1055,7 +1106,6 @@
onDensityChange();
}
});
-
}
private void onDensityChange() {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtSdkTestCase.java b/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtSdkTestCase.java
new file mode 100644
index 0000000..d884f35
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/AdtSdkTestCase.java
@@ -0,0 +1,60 @@
+/*
+ * 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.tests;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.sdk.LoadStatus;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+
+/**
+ * A test case which uses the Sdk loaded by the Adt plugin.
+ */
+public abstract class AdtSdkTestCase extends SdkTestCase {
+
+ protected AdtSdkTestCase() {
+ }
+
+ /**
+ * Gets the current Sdk from Adt, waiting if necessary.
+ */
+ @Override
+ protected Sdk loadSdk() {
+ AdtPlugin adt = AdtPlugin.getDefault();
+ Object sdkLock = adt.getSdkLockObject();
+ LoadStatus loadStatus = LoadStatus.LOADING;
+ // wait for Adt to load the Sdk on a separate thread
+ // loop max of 600 times * 200 ms = 2 minutes
+ final int maxWait = 600;
+ for (int i=0; i < maxWait && loadStatus == LoadStatus.LOADING; i++) {
+ try {
+ Thread.sleep(200);
+ }
+ catch (InterruptedException e) {
+ // ignore
+ }
+ synchronized(sdkLock) {
+ loadStatus = adt.getSdkLoadStatus();
+ }
+ }
+ Sdk sdk = null;
+ synchronized(sdkLock) {
+ assertEquals(LoadStatus.LOADED, loadStatus);
+ sdk = Sdk.getCurrent();
+ }
+ assertNotNull(sdk);
+ return sdk;
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/FuncTestCase.java b/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/FuncTestCase.java
deleted file mode 100644
index 344f9ee..0000000
--- a/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/FuncTestCase.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2008 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.tests;
-
-import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetParser;
-import com.android.ide.eclipse.adt.internal.sdk.Sdk;
-import com.android.sdklib.IAndroidTarget;
-
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.NullProgressMonitor;
-
-import junit.framework.TestCase;
-
-/**
- * Generic superclass for Eclipse Android functional test cases, that provides common facilities.
- */
-public class FuncTestCase extends TestCase {
-
- private String mOsSdkLocation;
- protected Sdk mSdk;
-
- /**
- * Initializes test SDK
- * <p/>
- * Fails test if environment variable "sdk_home" is not set.
- */
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mOsSdkLocation = System.getProperty("sdk_home");
- if (mOsSdkLocation == null) {
- mOsSdkLocation = System.getenv("sdk_home");
- }
- if (mOsSdkLocation == null || mOsSdkLocation.length() < 1) {
- fail("Environment variable sdk_home is not set");
- }
-
- loadSdk(mOsSdkLocation);
- }
-
- /**
- * Returns the absolute file system path of the Android SDK location to use for this test.
- */
- protected String getOsSdkLocation() {
- return mOsSdkLocation;
- }
-
- /**
- * Returns the {@link Sdk} under test.
- */
- protected Sdk getSdk() {
- return mSdk;
- }
-
- /**
- * Loads the {@link Sdk}.
- */
- private void loadSdk(String sdkLocation) {
- mSdk = Sdk.loadSdk(sdkLocation);
-
- int n = mSdk.getTargets().length;
- if (n > 0) {
- for (IAndroidTarget target : mSdk.getTargets()) {
- IStatus status = new AndroidTargetParser(target).run(new NullProgressMonitor());
- if (status.getCode() != IStatus.OK) {
- fail("Failed to parse targets data");
- }
- }
- }
- }
-}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkEnvTestCase.java b/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkEnvTestCase.java
new file mode 100644
index 0000000..1039a7f
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkEnvTestCase.java
@@ -0,0 +1,45 @@
+/*
+ * 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.tests;
+
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+
+
+/**
+ * A test case that receives a specific Sdk to test via the "sdk_home" environment variable.
+ */
+public abstract class SdkEnvTestCase extends SdkTestCase {
+
+ protected SdkEnvTestCase() {
+ }
+
+ /**
+ * Loads the {@link Sdk}.
+ * <p/>
+ * Fails test if environment variable "sdk_home" is not set.
+ */
+ @Override
+ protected Sdk loadSdk() {
+ String osSdkLocation = System.getProperty("sdk_home");
+ if (osSdkLocation == null) {
+ osSdkLocation = System.getenv("sdk_home");
+ }
+ if (osSdkLocation == null || osSdkLocation.length() < 1) {
+ fail("Environment variable sdk_home is not set");
+ }
+ return Sdk.loadSdk(osSdkLocation);
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkTestCase.java b/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkTestCase.java
new file mode 100644
index 0000000..322ce3e
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/SdkTestCase.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008 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.tests;
+
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetParser;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.IAndroidTarget;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+
+import junit.framework.TestCase;
+
+/**
+ * A test case that needs a reference to a SDK.
+ */
+public abstract class SdkTestCase extends TestCase {
+
+ private Sdk mSdk;
+
+ protected SdkTestCase() {
+ }
+
+ /**
+ * Retrieve the {@link Sdk} under test.
+ */
+ protected Sdk getSdk() {
+ if (mSdk == null) {
+ mSdk = loadSdk();
+ assertNotNull(mSdk);
+ validateSdk(mSdk);
+ }
+ return mSdk;
+ }
+
+ /**
+ * Loads the {@link Sdk} to use for test
+ */
+ protected abstract Sdk loadSdk();
+
+ /**
+ * Checks that the provided sdk contains one or more valid targets.
+ * @param sdk the {@link Sdk} to validate.
+ */
+ private void validateSdk(Sdk sdk) {
+ assertTrue("sdk has no targets", sdk.getTargets().length > 0);
+ for (IAndroidTarget target : sdk.getTargets()) {
+ IStatus status = new AndroidTargetParser(target).run(new NullProgressMonitor());
+ if (status.getCode() != IStatus.OK) {
+ fail("Failed to parse targets data");
+ }
+ }
+ }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java b/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java
index f4dafc8..3b52789 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/layoutRendering/ApiDemosRenderingTest.java
@@ -39,7 +39,7 @@
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.internal.sdk.LoadStatus;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.LayoutBridge;
-import com.android.ide.eclipse.tests.FuncTestCase;
+import com.android.ide.eclipse.tests.SdkEnvTestCase;
import com.android.layoutlib.api.ILayoutResult;
import com.android.layoutlib.api.IProjectCallback;
import com.android.layoutlib.api.IResourceValue;
@@ -59,7 +59,7 @@
import javax.imageio.ImageIO;
-public class ApiDemosRenderingTest extends FuncTestCase {
+public class ApiDemosRenderingTest extends SdkEnvTestCase {
/**
* Custom parser that implements {@link IXmlPullParser} (which itself extends
@@ -121,7 +121,7 @@
}
private void findApiDemos() throws IOException, XmlPullParserException {
- IAndroidTarget[] targets = mSdk.getTargets();
+ IAndroidTarget[] targets = getSdk().getTargets();
for (IAndroidTarget target : targets) {
String path = target.getPath(IAndroidTarget.SAMPLES);
@@ -141,7 +141,7 @@
}
private void testSample(IAndroidTarget target, File sampleProject) throws IOException, XmlPullParserException {
- AndroidTargetData data = mSdk.getTargetData(target);
+ AndroidTargetData data = getSdk().getTargetData(target);
if (data == null) {
fail("No AndroidData!");
}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/sampleProjects/SampleProjectTest.java b/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/sampleProjects/SampleProjectTest.java
index 4b7d014..89421ef 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/sampleProjects/SampleProjectTest.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/tests/functests/sampleProjects/SampleProjectTest.java
@@ -16,12 +16,13 @@
package com.android.ide.eclipse.tests.functests.sampleProjects;
import com.android.ide.eclipse.adt.AndroidConstants;
-import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
import com.android.ide.eclipse.adt.wizards.newproject.StubProjectWizard;
-import com.android.ide.eclipse.tests.FuncTestCase;
+import com.android.ide.eclipse.tests.AdtSdkTestCase;
import com.android.sdklib.IAndroidTarget;
+import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
@@ -43,7 +44,7 @@
* execution there
*
*/
-public class SampleProjectTest extends FuncTestCase {
+public class SampleProjectTest extends AdtSdkTestCase {
private static final Logger sLogger = Logger.getLogger(SampleProjectTest.class.getName());
@@ -56,7 +57,7 @@
public void testSamples() throws CoreException {
// TODO: For reporting purposes, it would be better if a separate test success or failure
// could be reported for each sample
- IAndroidTarget[] targets = mSdk.getTargets();
+ IAndroidTarget[] targets = getSdk().getTargets();
for (IAndroidTarget target : targets) {
doTestSamplesForTarget(target);
}
@@ -145,8 +146,25 @@
private void validateNoProblems(IProject iproject) throws CoreException {
waitForBuild(iproject);
- assertFalse(String.format("%s project has compile errors", iproject.getName()),
- ProjectHelper.hasError(iproject, true));
+
+ boolean hasErrors = false;
+ StringBuilder failureBuilder = new StringBuilder(String.format("%s project has errors:",
+ iproject.getName()));
+ IMarker[] markers = iproject.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
+ if (markers != null && markers.length > 0) {
+ // the project has marker(s). even though they are "problem" we
+ // don't know their severity. so we loop on them and figure if they
+ // are warnings or errors
+ for (IMarker m : markers) {
+ int s = m.getAttribute(IMarker.SEVERITY, -1);
+ if (s == IMarker.SEVERITY_ERROR) {
+ hasErrors = true;
+ failureBuilder.append("\n");
+ failureBuilder.append(m.getAttribute(IMarker.MESSAGE, ""));
+ }
+ }
+ }
+ assertFalse(failureBuilder.toString(), hasErrors);
}
/**
diff --git a/tools/eclipse/scripts/create_test_symlinks.sh b/tools/eclipse/scripts/create_test_symlinks.sh
index 5c12542..2b2405b 100755
--- a/tools/eclipse/scripts/create_test_symlinks.sh
+++ b/tools/eclipse/scripts/create_test_symlinks.sh
@@ -44,7 +44,7 @@
cp -v "prebuilt/common/kxml2/kxml2-2.3.0.jar" "$DEST/"
fi
- LIBS="layoutlib.jar sdkuilib.jar"
+ LIBS="layoutlib.jar"
NEED_MAKE="yes"
for LIB in $LIBS ; do
SRCJAR="out/host/windows-x86/framework/$LIB"
@@ -68,7 +68,6 @@
DEST=$BASE/unittests/com/android
cpdir $DEST development/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib
cpdir $DEST development/tools/sdkmanager/libs/sdklib/tests/com/android/sdklib
-cpdir $DEST development/tools/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib
DEST=$BASE/unittests/com/android/layoutlib
mkdir -p $DEST
diff --git a/tools/scripts/android_rules.xml b/tools/scripts/android_rules.xml
index a235716..675017c 100644
--- a/tools/scripts/android_rules.xml
+++ b/tools/scripts/android_rules.xml
@@ -182,7 +182,7 @@
<sequential>
<echo>Installing ${out.debug.package} onto default emulator or device...</echo>
<exec executable="${adb}" failonerror="true">
- <arg value="${adb.device.arg}" />
+ <arg line="${adb.device.arg}" />
<arg value="install" />
<arg value="-r" />
<arg path="${out.debug.package}" />
@@ -366,6 +366,7 @@
description="Uninstalls the application from a running emulator or device.">
<echo>Uninstalling ${manifest.package} from the default emulator or device...</echo>
<exec executable="${adb}" failonerror="true">
+ <arg line="${adb.device.arg}" />
<arg value="uninstall" />
<arg value="${manifest.package}" />
</exec>
diff --git a/tools/scripts/doc_source.properties b/tools/scripts/doc_source.properties
index e6d6915..fab394a 100644
--- a/tools/scripts/doc_source.properties
+++ b/tools/scripts/doc_source.properties
@@ -1,6 +1,5 @@
Pkg.UserSrc=false
-Platform.Version=Eclair
+Platform.Version=2.0
Pkg.Revision=1
-AndroidVersion.ApiLevel=4
-AndroidVersion.CodeName=Eclair
+AndroidVersion.ApiLevel=5
diff --git a/tools/scripts/platform_source.properties b/tools/scripts/platform_source.properties
index 148c364..01f7807 100644
--- a/tools/scripts/platform_source.properties
+++ b/tools/scripts/platform_source.properties
@@ -1,6 +1,5 @@
-Pkg.Desc=Android SDK Platform Eclair
+Pkg.Desc=Android SDK Platform 2.0_r1
Pkg.UserSrc=false
-Platform.Version=Eclair
+Platform.Version=2.0
Pkg.Revision=1
-AndroidVersion.ApiLevel=4
-AndroidVersion.CodeName=Eclair
+AndroidVersion.ApiLevel=5
diff --git a/tools/sdklauncher/.gitignore b/tools/sdklauncher/.gitignore
new file mode 100644
index 0000000..0c25b2a
--- /dev/null
+++ b/tools/sdklauncher/.gitignore
@@ -0,0 +1 @@
+images/android_icon.o
diff --git a/tools/sdklauncher/Android.mk b/tools/sdklauncher/Android.mk
new file mode 100644
index 0000000..3e92ea8
--- /dev/null
+++ b/tools/sdklauncher/Android.mk
@@ -0,0 +1,43 @@
+# Copyright 2009 The Android Open Source Project
+#
+# Android.mk for sdklauncher
+#
+# The "SDK Launcher" is for Windows only.
+# This simple .exe will sit at the root of the Windows SDK
+# and currently simply executes tools\android.bat.
+# Eventually it should simply replace the batch file.
+
+ifeq ($(HOST_OS),windows)
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ sdklauncher.c
+
+LOCAL_CFLAGS += -Wall -Wno-unused-parameter
+LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE -DSH_HISTORY
+LOCAL_MODULE := sdklauncher
+
+# Link the Windows icon file as well into the executable, based on the technique
+# used in external/qemu/Makefile.android.
+#
+INTERMEDIATE := $(call intermediates-dir-for,EXECUTABLES,$(LOCAL_MODULE),true)
+ANDROID_ICON_OBJ := android_icon.o
+ANDROID_ICON_PATH := $(LOCAL_PATH)/images
+$(ANDROID_ICON_PATH)/$(ANDROID_ICON_OBJ): $(ANDROID_ICON_PATH)/android_icon.rc
+ windres $< -I $(ANDROID_ICON_PATH) -o $@
+
+# seems to be the only way to add an object file that was not generated from
+# a C/C++/Java source file to our build system. and very unfortunately,
+# $(TOPDIR)/$(LOCALPATH) will always be prepended to this value, which forces
+# us to put the object file in the source directory...
+#
+LOCAL_PREBUILT_OBJ_FILES += images/$(ANDROID_ICON_OBJ)
+
+include $(BUILD_HOST_EXECUTABLE)
+
+$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE))
+
+endif
diff --git a/tools/sdklauncher/images/android_icon.ico b/tools/sdklauncher/images/android_icon.ico
new file mode 100644
index 0000000..bd25179
--- /dev/null
+++ b/tools/sdklauncher/images/android_icon.ico
Binary files differ
diff --git a/tools/sdklauncher/images/android_icon.rc b/tools/sdklauncher/images/android_icon.rc
new file mode 100644
index 0000000..df468ac
--- /dev/null
+++ b/tools/sdklauncher/images/android_icon.rc
@@ -0,0 +1,3 @@
+1 ICON "../images/android_icon.ico"
+
+
diff --git a/tools/sdklauncher/sdklauncher.c b/tools/sdklauncher/sdklauncher.c
new file mode 100644
index 0000000..d052284
--- /dev/null
+++ b/tools/sdklauncher/sdklauncher.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * The "SDK Launcher" is for Windows only.
+ * This simple .exe will sit at the root of the Windows SDK
+ * and currently simply executes tools\android.bat.
+ * Eventually it should simply replace the batch file.
+ *
+ * TODO:
+ * - detect that java is installed; error dialog if not, explaning where to get it.
+ * - create temp dir, always copy *.jar there, exec android.jar
+ * - get jars to copy from some file
+ * - use a version number to copy jars only if needed (tools.revision?)
+ */
+
+#ifdef _WIN32
+
+#include <stdio.h>
+#include <windows.h>
+
+int sdk_launcher() {
+ STARTUPINFO startup;
+ PROCESS_INFORMATION pinfo;
+ char program_path[MAX_PATH];
+ int ret;
+
+ ZeroMemory(&startup, sizeof(startup));
+ startup.cb = sizeof(startup);
+
+ ZeroMemory(&pinfo, sizeof(pinfo));
+
+ /* get path of current program */
+ GetModuleFileName(NULL, program_path, sizeof(program_path));
+
+ ret = CreateProcess(
+ NULL, /* program path */
+ "tools\\android.bat update sdk", /* command-line */
+ NULL, /* process handle is not inheritable */
+ NULL, /* thread handle is not inheritable */
+ TRUE, /* yes, inherit some handles */
+ CREATE_NO_WINDOW, /* we don't want a console */
+ NULL, /* use parent's environment block */
+ NULL, /* use parent's starting directory */
+ &startup, /* startup info, i.e. std handles */
+ &pinfo);
+
+ if (!ret) {
+ DWORD err = GetLastError();
+ fprintf(stderr, "CreateProcess failure, error %ld\n", err);
+
+ LPSTR s;
+ if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | /* dwFlags */
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, /* lpSource */
+ err, /* dwMessageId */
+ 0, /* dwLanguageId */
+ (LPSTR)&s, /* lpBuffer */
+ 0, /* nSize */
+ NULL) != 0) { /* va_list args */
+ fprintf(stderr, "%s", s);
+ LocalFree(s);
+ }
+
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ return sdk_launcher();
+}
+
+#endif /* _WIN32 */
diff --git a/tools/sdkmanager/app/etc/android.bat b/tools/sdkmanager/app/etc/android.bat
index 5a75dfa..239ed81 100755
--- a/tools/sdkmanager/app/etc/android.bat
+++ b/tools/sdkmanager/app/etc/android.bat
@@ -32,7 +32,9 @@
rem Set SWT.Jar path based on current architecture (x86 or x86_64)
for /f %%a in ('java -jar lib\archquery.jar') do set swt_path=lib\%%a
+if "%1 %2"=="update sdk" goto StartUi
if not "%1"=="" goto EndTempCopy
+:StartUi
echo Starting Android SDK and AVD Manager
rem We're now going to create a temp dir to hold all the Jar files needed
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java b/tools/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
index 2e21f7b..ab105ec 100644
--- a/tools/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/CommandLineProcessor.java
@@ -36,6 +36,16 @@
*/
class CommandLineProcessor {
+ /*
+ * Steps needed to add a new action:
+ * - Each action is defined as a "verb object" followed by parameters.
+ * - Either reuse a VERB_ constant or define a new one.
+ * - Either reuse an OBJECT_ constant or define a new one.
+ * - Add a new entry to mAction with a one-line help summary.
+ * - In the constructor, add a define() call for each parameter (either mandatory
+ * or optional) for the given action.
+ */
+
/** Internal verb name for internally hidden flags. */
public final static String GLOBAL_FLAG_VERB = "@@internal@@";
@@ -57,10 +67,14 @@
/**
* Action definitions.
* <p/>
+ * This list serves two purposes: first it is used to know which verb/object
+ * actions are acceptable on the command-line; second it provides a summary
+ * for each action that is printed in the help.
+ * <p/>
* Each entry is a string array with:
* <ul>
* <li> the verb.
- * <li> a direct object (use #NO_VERB_OBJECT if there's no object).
+ * <li> a direct object (use {@link #NO_VERB_OBJECT} if there's no object).
* <li> a description.
* <li> an alternate form for the object (e.g. plural).
* </ul>
@@ -81,6 +95,15 @@
/** Logger */
private final ISdkLog mLog;
+ /**
+ * Constructs a new command-line processor.
+ *
+ * @param logger An SDK logger object. Must not be null.
+ * @param actions The list of actions recognized on the command-line.
+ * See the javadoc of {@link #mActions} for more details.
+ *
+ * @see #mActions
+ */
public CommandLineProcessor(ISdkLog logger, String[][] actions) {
mLog = logger;
mActions = actions;
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
index 1c847fe..84195d1 100644
--- a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
@@ -34,6 +34,7 @@
import com.android.sdklib.xml.AndroidXPathFactory;
import com.android.sdkmanager.internal.repository.AboutPage;
import com.android.sdkmanager.internal.repository.SettingsPage;
+import com.android.sdkuilib.internal.repository.LocalPackagesPage;
import com.android.sdkuilib.repository.UpdaterWindow;
import org.xml.sax.InputSource;
@@ -243,7 +244,11 @@
updateTestProject();
} else if (verb == null && directObject == null) {
- showMainWindow();
+ showMainWindow(false /*autoUpdate*/);
+
+ } else if (SdkCommandLine.VERB_UPDATE.equals(verb) &&
+ SdkCommandLine.OBJECT_SDK.equals(directObject)) {
+ showMainWindow(true /*autoUpdate*/);
} else if (SdkCommandLine.VERB_UPDATE.equals(verb) &&
SdkCommandLine.OBJECT_ADB.equals(directObject)) {
@@ -257,7 +262,7 @@
/**
* Display the main SdkManager app window
*/
- private void showMainWindow() {
+ private void showMainWindow(boolean autoUpdate) {
try {
// display a message talking about the command line version
System.out.printf("No command line parameters provided, launching UI.\n" +
@@ -269,6 +274,10 @@
false /*userCanChangeSdkRoot*/);
window.registerPage("Settings", SettingsPage.class);
window.registerPage("About", AboutPage.class);
+ if (autoUpdate) {
+ window.setInitialPage(LocalPackagesPage.class);
+ window.setRequestAutoUpdate(true);
+ }
window.open();
} catch (Exception e) {
e.printStackTrace();
@@ -353,12 +362,14 @@
} catch (IOException e) {
errorAndExit("Unable to resolve Main project's directory: %1$s",
pathToMainProject);
+ return; // help Eclipse static analyzer understand we'll never execute the rest.
}
}
if (parentProject.isDirectory() == false) {
errorAndExit("Main project's directory does not exist: %1$s",
pathToMainProject);
+ return;
}
// now look for a manifest in there
@@ -366,6 +377,7 @@
if (manifest.isFile() == false) {
errorAndExit("No AndroidManifest.xml file found in the main project directory: %1$s",
parentProject.getAbsolutePath());
+ return;
}
// now query the manifest for the package file.
@@ -405,6 +417,7 @@
String targetHash = p.getProperty(ProjectProperties.PROPERTY_TARGET);
if (targetHash == null) {
errorAndExit("Couldn't find the main project target");
+ return;
}
// and resolve it.
@@ -413,6 +426,7 @@
errorAndExit(
"Unable to resolve main project target '%1$s'. You may want to install the platform in your SDK.",
targetHash);
+ return;
}
mSdkLog.printf("Found main project target: %1$s\n", target.getFullName());
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java b/tools/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
index 27afd48..80ee5dd 100644
--- a/tools/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java
@@ -25,12 +25,23 @@
*/
class SdkCommandLine extends CommandLineProcessor {
+ /*
+ * Steps needed to add a new action:
+ * - Each action is defined as a "verb object" followed by parameters.
+ * - Either reuse a VERB_ constant or define a new one.
+ * - Either reuse an OBJECT_ constant or define a new one.
+ * - Add a new entry to mAction with a one-line help summary.
+ * - In the constructor, add a define() call for each parameter (either mandatory
+ * or optional) for the given action.
+ */
+
public final static String VERB_LIST = "list";
public final static String VERB_CREATE = "create";
public final static String VERB_MOVE = "move";
public final static String VERB_DELETE = "delete";
public final static String VERB_UPDATE = "update";
+ public static final String OBJECT_SDK = "sdk";
public static final String OBJECT_AVD = "avd";
public static final String OBJECT_AVDS = "avds";
public static final String OBJECT_TARGET = "target";
@@ -59,6 +70,10 @@
/**
* Action definitions for SdkManager command line.
* <p/>
+ * This list serves two purposes: first it is used to know which verb/object
+ * actions are acceptable on the command-line; second it provides a summary
+ * for each action that is printed in the help.
+ * <p/>
* Each entry is a string array with:
* <ul>
* <li> the verb.
@@ -98,11 +113,16 @@
{ VERB_UPDATE, OBJECT_ADB,
"Updates adb to support the USB devices declared in the SDK add-ons." },
+
+ { VERB_UPDATE, OBJECT_SDK,
+ "Updates the SDK by suggesting new platforms to install if available." }
};
public SdkCommandLine(ISdkLog logger) {
super(logger, ACTIONS);
+ // The following defines the parameters of the actions defined in mAction.
+
// --- create avd ---
define(Mode.STRING, false,
@@ -175,6 +195,7 @@
"Project name", null);
// --- create test-project ---
+
define(Mode.STRING, true,
VERB_CREATE, OBJECT_TEST_PROJECT,
"p", KEY_PATH,
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
index 7b47f4a..9009274 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkManager.java
@@ -605,8 +605,10 @@
if (m.matches()) {
map.put(m.group(1), m.group(2));
} else {
- log.warning("Error parsing '%1$s': \"%2$s\" is not a valid syntax",
- buildProp.getAbsolutePath(), line);
+ if (log != null) {
+ log.warning("Error parsing '%1$s': \"%2$s\" is not a valid syntax",
+ buildProp.getAbsolutePath(), line);
+ }
return null;
}
}
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
index 2c9b3fb..ec8756f 100755
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/AddonPackage.java
@@ -189,18 +189,29 @@
/** Returns a short description for an {@link IDescription}. */
@Override
public String getShortDescription() {
- return String.format("%1$s by %2$s for Android API %3$s",
+ return String.format("%1$s by %2$s, Android API %3$s, revision %4$s",
getName(),
getVendor(),
- mVersion.getApiString());
+ mVersion.getApiString(),
+ getRevision());
}
- /** Returns a long description for an {@link IDescription}. */
+ /**
+ * Returns a long description for an {@link IDescription}.
+ *
+ * The long description is whatever the XML contains for the <description> field,
+ * or the short description if the former is empty.
+ */
@Override
public String getLongDescription() {
- return String.format("%1$s,\nRevision %2$d.",
- getShortDescription(),
- getRevision());
+ String s = getDescription();
+ if (s == null || s.length() == 0) {
+ s = getShortDescription();
+ }
+
+ s += String.format(".\nRequires SDK Platform Android API %1$s.",
+ mVersion.getApiString());
+ return s;
}
/**
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
index e54b550..58cccb2 100755
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/DocPackage.java
@@ -103,21 +103,34 @@
@Override
public String getShortDescription() {
if (mVersion.isPreview()) {
- return String.format("Documentation for Android '%1$s' Preview SDK",
- mVersion.getCodename());
- } else if (mVersion.getApiLevel() != 0) {
- return String.format("Documentation for Android SDK, API %1$d", mVersion.getApiLevel());
+ return String.format("Documentation for Android '%1$s' Preview SDK, revision %2$s",
+ mVersion.getCodename(),
+ getRevision());
} else {
- return String.format("Documentation for Android SDK");
+ return String.format("Documentation for Android SDK, API %1$d, revision %2$s",
+ mVersion.getApiLevel(),
+ getRevision());
}
}
- /** Returns a long description for an {@link IDescription}. */
+ /**
+ * Returns a long description for an {@link IDescription}.
+ *
+ * The long description is whatever the XML contains for the <description> field,
+ * or the short description if the former is empty.
+ */
@Override
public String getLongDescription() {
- return String.format("%1$s,\nRevision %2$d.",
- getShortDescription(),
- getRevision());
+ String s = getDescription();
+ if (s == null || s.length() == 0) {
+ s = getShortDescription();
+ }
+
+ if (!s.endsWith(".")) {
+ s += ".";
+ }
+
+ return s;
}
/**
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java
index 587be1d..e78f5fe 100755
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ExtraPackage.java
@@ -31,10 +31,9 @@
/**
* Represents a extra XML node in an SDK repository.
*/
-public class ExtraPackage extends Package {
+public class ExtraPackage extends MinToolsPackage {
private static final String PROP_PATH = "Extra.Path"; //$NON-NLS-1$
- private static final String PROP_MIN_TOOLS_REV = "Extra.MinToolsRev"; //$NON-NLS-1$
/**
* The install folder name. It must be a single-segment path.
@@ -45,27 +44,14 @@
private final String mPath;
/**
- * The minimal revision of the tools package required by this extra package, if > 0,
- * or {@link #MIN_TOOLS_REV_NOT_SPECIFIED} if there is no such requirement.
- */
- private final int mMinToolsRevision;
-
- /**
- * The value of {@link #mMinToolsRevision} when the {@link SdkRepository#NODE_MIN_TOOLS_REV}
- * was not specified in the XML source.
- */
- public static final int MIN_TOOLS_REV_NOT_SPECIFIED = 0;
-
- /**
* Creates a new tool package from the attributes and elements of the given XML node.
* <p/>
* This constructor should throw an exception if the package cannot be created.
*/
ExtraPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {
super(source, packageNode, licenses);
+
mPath = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_PATH);
- mMinToolsRevision = XmlParserUtils.getXmlInt(packageNode, SdkRepository.NODE_MIN_TOOLS_REV,
- MIN_TOOLS_REV_NOT_SPECIFIED);
}
/**
@@ -94,11 +80,9 @@
archiveOs,
archiveArch,
archiveOsPath);
+
// The path argument comes before whatever could be in the properties
mPath = path != null ? path : getProperty(props, PROP_PATH, path);
-
- mMinToolsRevision = Integer.parseInt(getProperty(props, PROP_MIN_TOOLS_REV,
- Integer.toString(MIN_TOOLS_REV_NOT_SPECIFIED)));
}
/**
@@ -111,8 +95,8 @@
props.setProperty(PROP_PATH, mPath);
- if (mMinToolsRevision != MIN_TOOLS_REV_NOT_SPECIFIED) {
- props.setProperty(PROP_MIN_TOOLS_REV, Integer.toString(mMinToolsRevision));
+ if (getMinToolsRevision() != MIN_TOOLS_REV_NOT_SPECIFIED) {
+ props.setProperty(PROP_MIN_TOOLS_REV, Integer.toString(getMinToolsRevision()));
}
}
@@ -139,14 +123,6 @@
return mPath;
}
- /**
- * The minimal revision of the tools package required by this extra package, if > 0,
- * or {@link #MIN_TOOLS_REV_NOT_SPECIFIED} if there is no such requirement.
- */
- public int getMinToolsRevision() {
- return mMinToolsRevision;
- }
-
/** Returns a short description for an {@link IDescription}. */
@Override
public String getShortDescription() {
@@ -176,25 +152,27 @@
name,
getRevision());
- if (mMinToolsRevision != MIN_TOOLS_REV_NOT_SPECIFIED) {
- s += String.format(" (tools rev: %1$d)", mMinToolsRevision);
- }
-
return s;
}
- /** Returns a long description for an {@link IDescription}. */
+ /**
+ * Returns a long description for an {@link IDescription}.
+ *
+ * The long description is whatever the XML contains for the <description> field,
+ * or the short description if the former is empty.
+ */
@Override
public String getLongDescription() {
- String s = String.format("Extra %1$s package, revision %2$d",
- getPath(),
- getRevision());
-
- if (mMinToolsRevision != MIN_TOOLS_REV_NOT_SPECIFIED) {
- s += String.format(" (min tools rev.: %1$d)", mMinToolsRevision);
+ String s = getDescription();
+ if (s == null || s.length() == 0) {
+ s = String.format("Extra %1$s package, revision %2$d",
+ getPath(),
+ getRevision());
}
- s += ".";
+ if (getMinToolsRevision() != MIN_TOOLS_REV_NOT_SPECIFIED) {
+ s += String.format(".\nRequires tools revision %1$d.", getMinToolsRevision());
+ }
return s;
}
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/MinToolsPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/MinToolsPackage.java
new file mode 100755
index 0000000..d5892f3
--- /dev/null
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/MinToolsPackage.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.sdklib.internal.repository;
+
+import com.android.sdklib.internal.repository.Archive.Arch;
+import com.android.sdklib.internal.repository.Archive.Os;
+import com.android.sdklib.repository.SdkRepository;
+
+import org.w3c.dom.Node;
+
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Represents an XML node in an SDK repository that has a min-tools-rev requirement.
+ * This is either a {@link PlatformPackage} or an {@link ExtraPackage}.
+ */
+public abstract class MinToolsPackage extends Package {
+
+ protected static final String PROP_MIN_TOOLS_REV = "Platform.MinToolsRev"; //$NON-NLS-1$
+
+ /**
+ * The minimal revision of the tools package required by this extra package, if > 0,
+ * or {@link #MIN_TOOLS_REV_NOT_SPECIFIED} if there is no such requirement.
+ */
+ private final int mMinToolsRevision;
+
+ /**
+ * The value of {@link #mMinToolsRevision} when the {@link SdkRepository#NODE_MIN_TOOLS_REV}
+ * was not specified in the XML source.
+ */
+ public static final int MIN_TOOLS_REV_NOT_SPECIFIED = 0;
+
+ /**
+ * Creates a new package from the attributes and elements of the given XML node.
+ * <p/>
+ * This constructor should throw an exception if the package cannot be created.
+ */
+ MinToolsPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {
+ super(source, packageNode, licenses);
+
+ mMinToolsRevision = XmlParserUtils.getXmlInt(packageNode, SdkRepository.NODE_MIN_TOOLS_REV,
+ MIN_TOOLS_REV_NOT_SPECIFIED);
+ }
+
+ /**
+ * Manually create a new package with one archive and the given attributes.
+ * This is used to create packages from local directories in which case there must be
+ * one archive which URL is the actual target location.
+ * <p/>
+ * Properties from props are used first when possible, e.g. if props is non null.
+ * <p/>
+ * By design, this creates a package with one and only one archive.
+ */
+ public MinToolsPackage(
+ RepoSource source,
+ Properties props,
+ int revision,
+ String license,
+ String description,
+ String descUrl,
+ Os archiveOs,
+ Arch archiveArch,
+ String archiveOsPath) {
+ super(source, props, revision, license, description, descUrl,
+ archiveOs, archiveArch, archiveOsPath);
+
+ mMinToolsRevision = Integer.parseInt(getProperty(props, PROP_MIN_TOOLS_REV,
+ Integer.toString(MIN_TOOLS_REV_NOT_SPECIFIED)));
+ }
+
+ /**
+ * The minimal revision of the tools package required by this extra package, if > 0,
+ * or {@link #MIN_TOOLS_REV_NOT_SPECIFIED} if there is no such requirement.
+ */
+ public int getMinToolsRevision() {
+ return mMinToolsRevision;
+ }
+}
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java
index e3fb3f2..cb39603 100755
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/PlatformPackage.java
@@ -33,10 +33,9 @@
/**
* Represents a platform XML node in an SDK repository.
*/
-public class PlatformPackage extends Package {
+public class PlatformPackage extends MinToolsPackage {
protected static final String PROP_VERSION = "Platform.Version"; //$NON-NLS-1$
- protected static final String PROP_MIN_TOOLS_REV = "Platform.MinToolsRev"; //$NON-NLS-1$
/** The package version, for platform, add-on and doc packages. */
private final AndroidVersion mVersion;
@@ -45,24 +44,13 @@
private final String mVersionName;
/**
- * The minimal revision of the tools package required by this extra package, if > 0,
- * or {@link #MIN_TOOLS_REV_NOT_SPECIFIED} if there is no such requirement.
- */
- private final int mMinToolsRevision;
-
- /**
- * The value of {@link #mMinToolsRevision} when the {@link SdkRepository#NODE_MIN_TOOLS_REV}
- * was not specified in the XML source.
- */
- public static final int MIN_TOOLS_REV_NOT_SPECIFIED = 0;
-
- /**
* Creates a new platform package from the attributes and elements of the given XML node.
* <p/>
* This constructor should throw an exception if the package cannot be created.
*/
PlatformPackage(RepoSource source, Node packageNode, Map<String,String> licenses) {
super(source, packageNode, licenses);
+
mVersionName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_VERSION);
int apiLevel = XmlParserUtils.getXmlInt (packageNode, SdkRepository.NODE_API_LEVEL, 0);
String codeName = XmlParserUtils.getXmlString(packageNode, SdkRepository.NODE_CODENAME);
@@ -70,9 +58,6 @@
codeName = null;
}
mVersion = new AndroidVersion(apiLevel, codeName);
-
- mMinToolsRevision = XmlParserUtils.getXmlInt(packageNode, SdkRepository.NODE_MIN_TOOLS_REV,
- MIN_TOOLS_REV_NOT_SPECIFIED);
}
/**
@@ -97,9 +82,6 @@
mVersion = target.getVersion();
mVersionName = target.getVersionName();
-
- mMinToolsRevision = Integer.parseInt(getProperty(props, PROP_MIN_TOOLS_REV,
- Integer.toString(MIN_TOOLS_REV_NOT_SPECIFIED)));
}
/**
@@ -116,8 +98,8 @@
props.setProperty(PROP_VERSION, mVersionName);
}
- if (mMinToolsRevision != MIN_TOOLS_REV_NOT_SPECIFIED) {
- props.setProperty(PROP_MIN_TOOLS_REV, Integer.toString(mMinToolsRevision));
+ if (getMinToolsRevision() != MIN_TOOLS_REV_NOT_SPECIFIED) {
+ props.setProperty(PROP_MIN_TOOLS_REV, Integer.toString(getMinToolsRevision()));
}
}
@@ -131,38 +113,43 @@
return mVersion;
}
- /**
- * The minimal revision of the tools package required by this extra package, if > 0,
- * or {@link #MIN_TOOLS_REV_NOT_SPECIFIED} if there is no such requirement.
- */
- public int getMinToolsRevision() {
- return mMinToolsRevision;
- }
-
/** Returns a short description for an {@link IDescription}. */
@Override
public String getShortDescription() {
String s;
if (mVersion.isPreview()) {
- s = String.format("SDK Platform Android %1$s (Preview)", getVersionName());
+ s = String.format("SDK Platform Android %1$s Preview, revision %2$s",
+ getVersionName(),
+ getRevision());
} else {
- s = String.format("SDK Platform Android %1$s, API %2$d",
+ s = String.format("SDK Platform Android %1$s, API %2$d, revision %3$s",
getVersionName(),
- mVersion.getApiLevel());
- }
-
- if (mMinToolsRevision != MIN_TOOLS_REV_NOT_SPECIFIED) {
- s += String.format(" (tools rev: %1$d)", mMinToolsRevision);
+ mVersion.getApiLevel(),
+ getRevision());
}
return s;
}
- /** Returns a long description for an {@link IDescription}. */
+ /**
+ * Returns a long description for an {@link IDescription}.
+ *
+ * The long description is whatever the XML contains for the <description> field,
+ * or the short description if the former is empty.
+ */
@Override
public String getLongDescription() {
- return getShortDescription() + ".";
+ String s = getDescription();
+ if (s == null || s.length() == 0) {
+ s = getShortDescription();
+ }
+
+ if (!s.endsWith(".")) {
+ s += ".";
+ }
+
+ return s;
}
/**
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java
index d4ab86f..833baac 100755
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/LocalPackagesPage.java
@@ -124,7 +124,7 @@
mUpdateButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
- onUpdateInstalledPackage(); //$hide$ (hide from SWT designer)
+ onUpdateSelected(); //$hide$ (hide from SWT designer)
}
});
@@ -255,7 +255,7 @@
mDescriptionLabel.setText(""); //$NON-NLS1-$
}
- private void onUpdateInstalledPackage() {
+ private void onUpdateSelected() {
mUpdaterData.updateOrInstallAll(null /*selectedArchives*/);
}
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java
index 1898f66..a2432f9 100755
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RemotePackagesPage.java
@@ -272,7 +272,6 @@
}
private void selectCompatibleArchives(Object pkg, ITreeContentProvider provider) {
- mTreeViewerSources.setExpandedState(pkg, true);
for (Object archive : provider.getChildren(pkg)) {
if (archive instanceof Archive) {
mTreeViewerSources.setChecked(archive, ((Archive) archive).isCompatible());
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java
index 9c6a80a..fafa9c1 100755
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/RepoSourcesAdapter.java
@@ -252,6 +252,9 @@
} else if (element instanceof Package) {
return ((Package) element).getParentSource();
+
+ } else if (element instanceof Archive) {
+ return ((Archive) element).getParentPackage();
}
return null;
}
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateChooserDialog.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateChooserDialog.java
index 5b8da83..652441b 100755
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateChooserDialog.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdateChooserDialog.java
Binary files differ
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java
index 1841421..a23ae39 100755
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterData.java
@@ -146,12 +146,14 @@
return mSettingsController;
}
+ /** Adds a listener ({@link ISdkListener}) that is notified when the SDK is reloaded. */
public void addListeners(ISdkListener listener) {
if (mListeners.contains(listener) == false) {
mListeners.add(listener);
}
}
+ /** Removes a listener ({@link ISdkListener}) that is notified when the SDK is reloaded. */
public void removeListener(ISdkListener listener) {
mListeners.remove(listener);
}
@@ -444,9 +446,15 @@
/**
* Tries to update all the *existing* local packages.
- * This first refreshes all sources, then compares the available remote packages with
- * the current local ones and suggest updates to be done to the user. Finally all
- * selected updates are installed.
+ * <p/>
+ * There are two modes of operation:
+ * <ul>
+ * <li>If selectedArchives is null, refreshes all sources, compares the available remote
+ * packages with the current local ones and suggest updates to be done to the user (including
+ * new platforms that the users doesn't have yet).
+ * <li>If selectedArchives is not null, this represents a list of archives/packages that
+ * the user wants to install or update, so just process these.
+ * </ul>
*
* @param selectedArchives The list of remote archive to consider for the update.
* This can be null, in which case a list of remote archive is fetched from all
@@ -463,6 +471,13 @@
getSources(),
getLocalSdkParser().getPackages());
+ if (selectedArchives == null) {
+ ul.addNewPlatforms(archives, getSources(), getLocalSdkParser().getPackages());
+ }
+
+ // TODO if selectedArchives is null and archives.len==0, find if there's
+ // any new platform we can suggest to install instead.
+
UpdateChooserDialog dialog = new UpdateChooserDialog(getWindowShell(), this, archives);
dialog.open();
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java
index 6d9935d..df04de2 100755
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterLogic.java
@@ -19,6 +19,8 @@
import com.android.sdklib.AndroidVersion;
import com.android.sdklib.internal.repository.AddonPackage;
import com.android.sdklib.internal.repository.Archive;
+import com.android.sdklib.internal.repository.ExtraPackage;
+import com.android.sdklib.internal.repository.MinToolsPackage;
import com.android.sdklib.internal.repository.Package;
import com.android.sdklib.internal.repository.PlatformPackage;
import com.android.sdklib.internal.repository.RepoSource;
@@ -28,39 +30,150 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
+/**
+ * The logic to compute which packages to install, based on the choices
+ * made by the user. This adds dependent packages as needed.
+ * <p/>
+ * When the user doesn't provide a selection, looks at local package to find
+ * those that can be updated and compute dependencies too.
+ */
class UpdaterLogic {
- private RepoSources mSources;
-
+ /**
+ * Compute which packages to install by taking the user selection
+ * and adding dependent packages as needed.
+ *
+ * When the user doesn't provide a selection, looks at local package to find
+ * those that can be updated and compute dependencies too.
+ */
public ArrayList<ArchiveInfo> computeUpdates(
Collection<Archive> selectedArchives,
RepoSources sources,
Package[] localPkgs) {
- mSources = sources;
ArrayList<ArchiveInfo> archives = new ArrayList<ArchiveInfo>();
ArrayList<Package> remotePkgs = new ArrayList<Package>();
+ RepoSource[] remoteSources = sources.getSources();
if (selectedArchives == null) {
- selectedArchives = findUpdates(localPkgs, remotePkgs);
+ selectedArchives = findUpdates(localPkgs, remotePkgs, remoteSources);
}
for (Archive a : selectedArchives) {
- insertArchive(a, archives, selectedArchives, remotePkgs, localPkgs, false);
+ insertArchive(a,
+ archives,
+ selectedArchives,
+ remotePkgs,
+ remoteSources,
+ localPkgs,
+ false /*automated*/);
}
return archives;
}
+ /**
+ * Finds new platforms that the user does not have in his/her local SDK
+ * and adds them to the list of archives to install.
+ */
+ public void addNewPlatforms(ArrayList<ArchiveInfo> archives,
+ RepoSources sources,
+ Package[] localPkgs) {
+ // Find the highest platform installed
+ float currentPlatformScore = 0;
+ float currentAddonScore = 0;
+ HashMap<String, Float> currentExtraScore = new HashMap<String, Float>();
+ for (Package p : localPkgs) {
+ int rev = p.getRevision();
+ int api = 0;
+ boolean isPreview = false;
+ if (p instanceof PlatformPackage) {
+ AndroidVersion vers = ((PlatformPackage) p).getVersion();
+ api = vers.getApiLevel();
+ isPreview = vers.isPreview();
+ } else if (p instanceof AddonPackage) {
+ AndroidVersion vers = ((AddonPackage) p).getVersion();
+ api = vers.getApiLevel();
+ isPreview = vers.isPreview();
+ } else if (!(p instanceof ExtraPackage)) {
+ continue;
+ }
+
+ // The score is 10*api + (1 if preview) + rev/100
+ // This allows previews to rank above a non-preview and
+ // allows revisions to rank appropriately.
+ float score = api * 10 + (isPreview ? 1 : 0) + rev/100.f;
+
+ if (p instanceof PlatformPackage) {
+ currentPlatformScore = Math.max(currentPlatformScore, score);
+ } else if (p instanceof AddonPackage) {
+ currentAddonScore = Math.max(currentAddonScore, score);
+ } else if (p instanceof ExtraPackage) {
+ currentExtraScore.put(((ExtraPackage) p).getPath(), score);
+ }
+ }
+
+ RepoSource[] remoteSources = sources.getSources();
+ ArrayList<Package> remotePkgs = new ArrayList<Package>();
+ fetchRemotePackages(remotePkgs, remoteSources);
+
+ for (Package p : remotePkgs) {
+ int rev = p.getRevision();
+ int api = 0;
+ boolean isPreview = false;
+ if (p instanceof PlatformPackage) {
+ AndroidVersion vers = ((PlatformPackage) p).getVersion();
+ api = vers.getApiLevel();
+ isPreview = vers.isPreview();
+ } else if (p instanceof AddonPackage) {
+ AndroidVersion vers = ((AddonPackage) p).getVersion();
+ api = vers.getApiLevel();
+ isPreview = vers.isPreview();
+ } else if (!(p instanceof ExtraPackage)) {
+ continue;
+ }
+
+ float score = api * 10 + (isPreview ? 1 : 0) + rev/100.f;
+
+ boolean shouldAdd = false;
+ if (p instanceof PlatformPackage) {
+ shouldAdd = score > currentPlatformScore;
+ } else if (p instanceof AddonPackage) {
+ shouldAdd = score > currentAddonScore;
+ } else if (p instanceof ExtraPackage) {
+ String key = ((ExtraPackage) p).getPath();
+ shouldAdd = !currentExtraScore.containsKey(key) ||
+ score > currentExtraScore.get(key).floatValue();
+ }
+
+ if (shouldAdd) {
+ // We should suggest this package for installation.
+ for (Archive a : p.getArchives()) {
+ if (a.isCompatible()) {
+ insertArchive(a,
+ archives,
+ null /*selectedArchives*/,
+ remotePkgs,
+ remoteSources,
+ localPkgs,
+ true /*automated*/);
+ }
+ }
+ }
+ }
+ }
/**
* Find suitable updates to all current local packages.
*/
- private Collection<Archive> findUpdates(Package[] localPkgs, ArrayList<Package> remotePkgs) {
+ private Collection<Archive> findUpdates(Package[] localPkgs,
+ ArrayList<Package> remotePkgs,
+ RepoSource[] remoteSources) {
ArrayList<Archive> updates = new ArrayList<Archive>();
- fetchRemotePackages(remotePkgs);
+ fetchRemotePackages(remotePkgs, remoteSources);
for (Package localPkg : localPkgs) {
for (Package remotePkg : remotePkgs) {
@@ -85,6 +198,7 @@
ArrayList<ArchiveInfo> outArchives,
Collection<Archive> selectedArchives,
ArrayList<Package> remotePkgs,
+ RepoSource[] remoteSources,
Package[] localPkgs,
boolean automated) {
Package p = archive.getParentPackage();
@@ -99,15 +213,32 @@
}
// find dependencies
- ArchiveInfo dep = findDependency(p, outArchives, selectedArchives, remotePkgs, localPkgs);
+ ArchiveInfo dep = findDependency(p,
+ outArchives,
+ selectedArchives,
+ remotePkgs,
+ remoteSources,
+ localPkgs);
- ArchiveInfo ai = new ArchiveInfo(
+ // Make sure it's not a dup
+ ArchiveInfo ai = null;
+
+ for (ArchiveInfo ai2 : outArchives) {
+ if (ai2.getNewArchive().getParentPackage().sameItemAs(archive.getParentPackage())) {
+ ai = ai2;
+ break;
+ }
+ }
+
+ if (ai == null) {
+ ai = new ArchiveInfo(
archive, //newArchive
updatedArchive, //replaced
dep //dependsOn
);
+ outArchives.add(ai);
+ }
- outArchives.add(ai);
if (dep != null) {
dep.addDependencyFor(ai);
}
@@ -119,6 +250,7 @@
ArrayList<ArchiveInfo> outArchives,
Collection<Archive> selectedArchives,
ArrayList<Package> remotePkgs,
+ RepoSource[] remoteSources,
Package[] localPkgs) {
// Current dependencies can be:
@@ -128,34 +260,47 @@
if (pkg instanceof AddonPackage) {
AddonPackage addon = (AddonPackage) pkg;
- return findAddonDependency(
- addon, outArchives, selectedArchives, remotePkgs, localPkgs);
-
- } else if (pkg instanceof PlatformPackage) {
- PlatformPackage platform = (PlatformPackage) pkg;
-
return findPlatformDependency(
- platform, outArchives, selectedArchives, remotePkgs, localPkgs);
+ addon,
+ outArchives,
+ selectedArchives,
+ remotePkgs,
+ remoteSources,
+ localPkgs);
+
+ } else if (pkg instanceof MinToolsPackage) {
+ MinToolsPackage platformOrExtra = (MinToolsPackage) pkg;
+
+ return findToolsDependency(
+ platformOrExtra,
+ outArchives,
+ selectedArchives,
+ remotePkgs,
+ remoteSources,
+ localPkgs);
}
return null;
}
/**
- * A platform can have a min-tools-rev, in which case it depends on having a tools package
- * of the requested revision.
+ * Resolves dependencies on tools.
+ *
+ * A platform or an extra package can both have a min-tools-rev, in which case it
+ * depends on having a tools package of the requested revision.
* Finds the tools dependency. If found, add it to the list of things to install.
* Returns the archive info dependency, if any.
*/
- protected ArchiveInfo findPlatformDependency(PlatformPackage platform,
+ protected ArchiveInfo findToolsDependency(MinToolsPackage platformOrExtra,
ArrayList<ArchiveInfo> outArchives,
Collection<Archive> selectedArchives,
ArrayList<Package> remotePkgs,
+ RepoSource[] remoteSources,
Package[] localPkgs) {
// This is the requirement to match.
- int rev = platform.getMinToolsRevision();
+ int rev = platformOrExtra.getMinToolsRevision();
- if (rev == PlatformPackage.MIN_TOOLS_REV_NOT_SPECIFIED) {
+ if (rev == MinToolsPackage.MIN_TOOLS_REV_NOT_SPECIFIED) {
// Well actually there's no requirement.
return null;
}
@@ -183,20 +328,22 @@
}
// Otherwise look in the selected archives.
- for (Archive a : selectedArchives) {
- Package p = a.getParentPackage();
- if (p instanceof ToolPackage) {
- if (((ToolPackage) p).getRevision() >= rev) {
- // It's not already in the list of things to install, so add it now
- return insertArchive(a, outArchives,
- selectedArchives, remotePkgs, localPkgs,
- true);
+ if (selectedArchives != null) {
+ for (Archive a : selectedArchives) {
+ Package p = a.getParentPackage();
+ if (p instanceof ToolPackage) {
+ if (((ToolPackage) p).getRevision() >= rev) {
+ // It's not already in the list of things to install, so add it now
+ return insertArchive(a, outArchives,
+ selectedArchives, remotePkgs, remoteSources, localPkgs,
+ true /*automated*/);
+ }
}
}
}
// Finally nothing matched, so let's look at all available remote packages
- fetchRemotePackages(remotePkgs);
+ fetchRemotePackages(remotePkgs, remoteSources);
for (Package p : remotePkgs) {
if (p instanceof ToolPackage) {
if (((ToolPackage) p).getRevision() >= rev) {
@@ -205,8 +352,8 @@
for (Archive a : p.getArchives()) {
if (a.isCompatible()) {
return insertArchive(a, outArchives,
- selectedArchives, remotePkgs, localPkgs,
- true);
+ selectedArchives, remotePkgs, remoteSources, localPkgs,
+ true /*automated*/);
}
}
}
@@ -220,14 +367,17 @@
}
/**
+ * Resolves dependencies on platform.
+ *
* An addon depends on having a platform with the same API version.
* Finds the platform dependency. If found, add it to the list of things to install.
* Returns the archive info dependency, if any.
*/
- protected ArchiveInfo findAddonDependency(AddonPackage addon,
+ protected ArchiveInfo findPlatformDependency(AddonPackage addon,
ArrayList<ArchiveInfo> outArchives,
Collection<Archive> selectedArchives,
ArrayList<Package> remotePkgs,
+ RepoSource[] remoteSources,
Package[] localPkgs) {
// This is the requirement to match.
AndroidVersion v = addon.getVersion();
@@ -257,20 +407,22 @@
}
// Otherwise look in the selected archives.
- for (Archive a : selectedArchives) {
- Package p = a.getParentPackage();
- if (p instanceof PlatformPackage) {
- if (v.equals(((PlatformPackage) p).getVersion())) {
- // It's not already in the list of things to install, so add it now
- return insertArchive(a, outArchives,
- selectedArchives, remotePkgs, localPkgs,
- true);
+ if (selectedArchives != null) {
+ for (Archive a : selectedArchives) {
+ Package p = a.getParentPackage();
+ if (p instanceof PlatformPackage) {
+ if (v.equals(((PlatformPackage) p).getVersion())) {
+ // It's not already in the list of things to install, so add it now
+ return insertArchive(a, outArchives,
+ selectedArchives, remotePkgs, remoteSources, localPkgs,
+ true /*automated*/);
+ }
}
}
}
// Finally nothing matched, so let's look at all available remote packages
- fetchRemotePackages(remotePkgs);
+ fetchRemotePackages(remotePkgs, remoteSources);
for (Package p : remotePkgs) {
if (p instanceof PlatformPackage) {
if (v.equals(((PlatformPackage) p).getVersion())) {
@@ -279,8 +431,8 @@
for (Archive a : p.getArchives()) {
if (a.isCompatible()) {
return insertArchive(a, outArchives,
- selectedArchives, remotePkgs, localPkgs,
- true);
+ selectedArchives, remotePkgs, remoteSources, localPkgs,
+ true /*automated*/);
}
}
}
@@ -296,14 +448,11 @@
}
/** Fetch all remote packages only if really needed. */
- protected void fetchRemotePackages(ArrayList<Package> remotePkgs) {
+ protected void fetchRemotePackages(ArrayList<Package> remotePkgs, RepoSource[] remoteSources) {
if (remotePkgs.size() > 0) {
return;
}
- // Get all the available packages from all loaded sources
- RepoSource[] remoteSources = mSources.getSources();
-
for (RepoSource remoteSrc : remoteSources) {
Package[] pkgs = remoteSrc.getPackages();
if (pkgs != null) {
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java
index 247f0a6..6e24af4 100755
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl.java
@@ -60,7 +60,11 @@
private ArrayList<Object[]> mExtraPages;
/** A factory to create progress task dialogs. */
private ProgressTaskFactory mTaskFactory;
-
+ /** The initial page to display. If null or not a know class, the first page will be displayed.
+ * Must be set before the first call to {@link #open()}. */
+ private Class<? extends Composite> mInitialPage;
+ /** Sets whether the auto-update wizard will be shown when opening the window. */
+ private boolean mRequestAutoUpdate;
// --- UI members ---
@@ -148,7 +152,7 @@
// Hide everything down-below from SWT designer
//$hide>>$
- // --- UI Callbacks -----------
+ // --- Public API -----------
/**
@@ -169,14 +173,42 @@
mExtraPages.add(new Object[]{ title, pageClass });
}
+ /**
+ * Indicate the initial page that should be selected when the window opens.
+ * This must be called before the call to {@link #open()}.
+ * If null or if the page class is not found, the first page will be selected.
+ */
+ public void setInitialPage(Class<? extends Composite> pageClass) {
+ mInitialPage = pageClass;
+ }
+
+ /**
+ * Sets whether the auto-update wizard will be shown when opening the window.
+ * <p/>
+ * This must be called before the call to {@link #open()}.
+ */
+ public void setRequestAutoUpdate(boolean requestAutoUpdate) {
+ mRequestAutoUpdate = requestAutoUpdate;
+ }
+
+ /**
+ * Adds a new listener to be notified when a change is made to the content of the SDK.
+ */
public void addListeners(ISdkListener listener) {
mUpdaterData.addListeners(listener);
}
+ /**
+ * Removes a new listener to be notified anymore when a change is made to the content of
+ * the SDK.
+ */
public void removeListener(ISdkListener listener) {
mUpdaterData.removeListener(listener);
}
+ // --- Internals & UI Callbacks -----------
+
+
/**
* Helper to return the SWT shell.
*/
@@ -230,12 +262,25 @@
addPage(mRemotePackagesPage, "Available Packages");
addExtraPages();
- displayPage(0);
- mPageList.setSelection(0);
+ int pageIndex = 0;
+ int i = 0;
+ for (Composite p : mPages) {
+ if (p.getClass().equals(mInitialPage)) {
+ pageIndex = i;
+ break;
+ }
+ i++;
+ }
+ displayPage(pageIndex);
+ mPageList.setSelection(pageIndex);
setupSources();
initializeSettings();
mUpdaterData.notifyListeners();
+
+ if (mRequestAutoUpdate) {
+ mUpdaterData.updateOrInstallAll(null /*selectedArchives*/);
+ }
}
/**
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java
index fbe11ef..20bdfad 100644
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java
@@ -316,7 +316,7 @@
});
mSkinSizeRadio = new Button(skinGroup, SWT.RADIO);
- mSkinSizeRadio.setText("Size:");
+ mSkinSizeRadio.setText("Resolution:");
mSkinSizeWidth = new Text(skinGroup, SWT.BORDER);
mSkinSizeWidth.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java
index abc36fd..75ec1c9 100644
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java
@@ -21,7 +21,6 @@
import com.android.sdkuilib.internal.repository.SettingsController;
import com.android.sdkuilib.ui.GridDialog;
-import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
@@ -35,7 +34,6 @@
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java
index 94881dc..7b56067 100755
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/repository/UpdaterWindow.java
@@ -70,6 +70,25 @@
}
/**
+ * Indicate the initial page that should be selected when the window opens.
+ * <p/>
+ * This must be called before the call to {@link #open()}.
+ * If null or if the page class is not found, the first page will be selected.
+ */
+ public void setInitialPage(Class<? extends Composite> pageClass) {
+ mWindow.setInitialPage(pageClass);
+ }
+
+ /**
+ * Sets whether the auto-update wizard will be shown when opening the window.
+ * <p/>
+ * This must be called before the call to {@link #open()}.
+ */
+ public void setRequestAutoUpdate(boolean requestAutoUpdate) {
+ mWindow.setRequestAutoUpdate(requestAutoUpdate);
+ }
+
+ /**
* Adds a new listener to be notified when a change is made to the content of the SDK.
*/
public void addListeners(ISdkListener listener) {
diff --git a/tools/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java b/tools/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java
index bb1f52e..3e22b35 100755
--- a/tools/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java
+++ b/tools/sdkmanager/libs/sdkuilib/tests/com/android/sdkuilib/internal/repository/UpdaterLogicTest.java
@@ -21,6 +21,7 @@
import com.android.sdklib.internal.repository.MockPlatformPackage;
import com.android.sdklib.internal.repository.MockToolPackage;
import com.android.sdklib.internal.repository.Package;
+import com.android.sdklib.internal.repository.RepoSource;
import java.util.ArrayList;
import java.util.Arrays;
@@ -37,7 +38,10 @@
}
@Override
- protected void fetchRemotePackages(ArrayList<Package> remotePkgs) {
+ protected void fetchRemotePackages(ArrayList<Package> remotePkgs,
+ RepoSource[] remoteSources) {
+ // Ignore remoteSources and instead uses the remotePackages list given to the
+ // constructor.
if (mRemotePackages != null) {
remotePkgs.addAll(Arrays.asList(mRemotePackages));
}
@@ -59,13 +63,14 @@
// a2 depends on p2, which is not in the locals
Package[] locals = { p1, a1 };
- assertNull(mul.findAddonDependency(a2, out, selected, remote, locals));
+ RepoSource[] sources = null;
+ assertNull(mul.findPlatformDependency(a2, out, selected, remote, sources, locals));
assertEquals(0, out.size());
// p2 is now selected, and should be scheduled for install in out
Archive p2_archive = p2.getArchives()[0];
selected.add(p2_archive);
- ArchiveInfo ai2 = mul.findAddonDependency(a2, out, selected, remote, locals);
+ ArchiveInfo ai2 = mul.findPlatformDependency(a2, out, selected, remote, sources, locals);
assertNotNull(ai2);
assertSame(p2_archive, ai2.getNewArchive());
assertEquals(1, out.size());
@@ -86,13 +91,14 @@
// p2 depends on t2, which is not locally installed
Package[] locals = { t1 };
- assertNull(mul.findPlatformDependency(p2, out, selected, remote, locals));
+ RepoSource[] sources = null;
+ assertNull(mul.findToolsDependency(p2, out, selected, remote, sources, locals));
assertEquals(0, out.size());
// t2 is now selected and can be used as a dependency
Archive t2_archive = t2.getArchives()[0];
selected.add(t2_archive);
- ArchiveInfo ai2 = mul.findPlatformDependency(p2, out, selected, remote, locals);
+ ArchiveInfo ai2 = mul.findToolsDependency(p2, out, selected, remote, sources, locals);
assertNotNull(ai2);
assertSame(t2_archive, ai2.getNewArchive());
assertEquals(1, out.size());