diff --git a/emulator/keymaps/AVRCP.kl b/emulator/keymaps/AVRCP.kl new file mode 100644 index 000000000..175824ef7 --- /dev/null +++ b/emulator/keymaps/AVRCP.kl @@ -0,0 +1,7 @@ +key 164 PLAYPAUSE WAKE +key 128 STOP WAKE +key 163 NEXTSONG WAKE +key 165 PREVIOUSSONG WAKE +key 168 REWIND WAKE +key 159 FORWARD WAKE + diff --git a/emulator/keymaps/Android.mk b/emulator/keymaps/Android.mk index 90db52ed4..81ac53000 100644 --- a/emulator/keymaps/Android.mk +++ b/emulator/keymaps/Android.mk @@ -11,3 +11,8 @@ file := $(TARGET_OUT_KEYLAYOUT)/qwerty.kl ALL_PREBUILT += $(file) $(file): $(LOCAL_PATH)/qwerty.kl | $(ACP) $(transform-prebuilt-to-target) + +file := $(TARGET_OUT_KEYLAYOUT)/AVRCP.kl +ALL_PREBUILT += $(file) +$(file) : $(LOCAL_PATH)/AVRCP.kl | $(ACP) + $(transform-prebuilt-to-target) diff --git a/host/windows/prebuilt/adb.exe b/host/windows/prebuilt/adb.exe deleted file mode 100755 index 4cba503e1..000000000 Binary files a/host/windows/prebuilt/adb.exe and /dev/null differ diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml index afcfbc88f..0cbba1449 100644 --- a/samples/ApiDemos/AndroidManifest.xml +++ b/samples/ApiDemos/AndroidManifest.xml @@ -1653,18 +1653,18 @@ - + - - + + - + - + diff --git a/samples/ApiDemos/res/layout/gadget_configure.xml b/samples/ApiDemos/res/layout/appwidget_configure.xml similarity index 92% rename from samples/ApiDemos/res/layout/gadget_configure.xml rename to samples/ApiDemos/res/layout/appwidget_configure.xml index bc9f40dc8..c041ad64b 100644 --- a/samples/ApiDemos/res/layout/gadget_configure.xml +++ b/samples/ApiDemos/res/layout/appwidget_configure.xml @@ -23,11 +23,11 @@ diff --git a/samples/ApiDemos/res/layout/gadget_provider.xml b/samples/ApiDemos/res/layout/appwidget_provider.xml similarity index 95% rename from samples/ApiDemos/res/layout/gadget_provider.xml rename to samples/ApiDemos/res/layout/appwidget_provider.xml index 49cf42f82..373db8f7d 100644 --- a/samples/ApiDemos/res/layout/gadget_provider.xml +++ b/samples/ApiDemos/res/layout/appwidget_provider.xml @@ -15,7 +15,7 @@ --> listSeparatorTextViewStyle - + - This text will be shown before the date in our example gadget. - Oh hai - %1$s: %2$s + This text will be shown before the date in our example widget. + Oh hai + %1$s: %2$s diff --git a/samples/ApiDemos/res/xml/gadget_provider.xml b/samples/ApiDemos/res/xml/appwidget_provider.xml similarity index 78% rename from samples/ApiDemos/res/xml/gadget_provider.xml rename to samples/ApiDemos/res/xml/appwidget_provider.xml index 9ad6845f4..5f5c7351e 100644 --- a/samples/ApiDemos/res/xml/gadget_provider.xml +++ b/samples/ApiDemos/res/xml/appwidget_provider.xml @@ -14,14 +14,14 @@ limitations under the License. --> - - + diff --git a/samples/ApiDemos/src/com/example/android/apis/gadget/ExampleGadgetConfigure.java b/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java similarity index 61% rename from samples/ApiDemos/src/com/example/android/apis/gadget/ExampleGadgetConfigure.java rename to samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java index 03e7bb42f..e0a4c76b0 100644 --- a/samples/ApiDemos/src/com/example/android/apis/gadget/ExampleGadgetConfigure.java +++ b/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.example.android.apis.gadget; +package com.example.android.apis.appwidget; import android.app.Activity; +import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.gadget.GadgetManager; import android.os.Bundle; import android.util.Log; import android.view.View; @@ -33,19 +33,19 @@ import java.util.ArrayList; import com.example.android.apis.R; /** - * The configuration screen for the ExampleGadgetProvider gadget sample. + * The configuration screen for the ExampleAppWidgetProvider widget sample. */ -public class ExampleGadgetConfigure extends Activity { - static final String TAG = "ExampleGadgetConfigure"; +public class ExampleAppWidgetConfigure extends Activity { + static final String TAG = "ExampleAppWidgetConfigure"; private static final String PREFS_NAME - = "com.example.android.apis.gadget.ExampleGadgetProvider"; + = "com.example.android.apis.appwidget.ExampleAppWidgetProvider"; private static final String PREF_PREFIX_KEY = "prefix_"; - int mGadgetId = GadgetManager.INVALID_GADGET_ID; - EditText mGadgetPrefix; + int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; + EditText mAppWidgetPrefix; - public ExampleGadgetConfigure() { + public ExampleAppWidgetConfigure() { super(); } @@ -53,70 +53,70 @@ public class ExampleGadgetConfigure extends Activity { public void onCreate(Bundle icicle) { super.onCreate(icicle); - // Set the result to CANCELED. This will cause the gadget host to cancel - // out of the gadget placement if they press the back button. + // Set the result to CANCELED. This will cause the widget host to cancel + // out of the widget placement if they press the back button. setResult(RESULT_CANCELED); // Set the view layout resource to use. - setContentView(R.layout.gadget_configure); + setContentView(R.layout.appwidget_configure); // Find the EditText - mGadgetPrefix = (EditText)findViewById(R.id.gadget_prefix); + mAppWidgetPrefix = (EditText)findViewById(R.id.appwidget_prefix); // Bind the action for the save button. findViewById(R.id.save_button).setOnClickListener(mOnClickListener); - // Find the gadget id from the intent. + // Find the widget id from the intent. Intent intent = getIntent(); Bundle extras = intent.getExtras(); if (extras != null) { - mGadgetId = extras.getInt( - GadgetManager.EXTRA_GADGET_ID, GadgetManager.INVALID_GADGET_ID); + mAppWidgetId = extras.getInt( + AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); } - // If they gave us an intent without the gadget id, just bail. - if (mGadgetId == GadgetManager.INVALID_GADGET_ID) { + // If they gave us an intent without the widget id, just bail. + if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { finish(); } - mGadgetPrefix.setText(loadTitlePref(ExampleGadgetConfigure.this, mGadgetId)); + mAppWidgetPrefix.setText(loadTitlePref(ExampleAppWidgetConfigure.this, mAppWidgetId)); } View.OnClickListener mOnClickListener = new View.OnClickListener() { public void onClick(View v) { // When the button is clicked, save the string in our prefs and return that they // clicked OK. - saveTitlePref(ExampleGadgetConfigure.this, mGadgetId, - mGadgetPrefix.getText().toString()); + saveTitlePref(ExampleAppWidgetConfigure.this, mAppWidgetId, + mAppWidgetPrefix.getText().toString()); setResult(RESULT_OK); finish(); } }; - // Write the prefix to the SharedPreferences object for this gadget - static void saveTitlePref(Context context, int gadgetId, String text) { + // Write the prefix to the SharedPreferences object for this widget + static void saveTitlePref(Context context, int appWidgetId, String text) { SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit(); - prefs.putString(PREF_PREFIX_KEY + gadgetId, text); + prefs.putString(PREF_PREFIX_KEY + appWidgetId, text); prefs.commit(); } - // Read the prefix from the SharedPreferences object for this gadget. + // Read the prefix from the SharedPreferences object for this widget. // If there is no preference saved, get the default from a resource - static String loadTitlePref(Context context, int gadgetId) { + static String loadTitlePref(Context context, int appWidgetId) { SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0); String prefix = prefs.getString(PREF_PREFIX_KEY, null); if (prefix != null) { return prefix; } else { - return context.getString(R.string.gadget_prefix_default); + return context.getString(R.string.appwidget_prefix_default); } } - static void deleteTitlePref(Context context, int gadgetId) { + static void deleteTitlePref(Context context, int appWidgetId) { } - static void loadAllTitlePrefs(Context context, ArrayList gadgetIds, + static void loadAllTitlePrefs(Context context, ArrayList appWidgetIds, ArrayList texts) { } } diff --git a/samples/ApiDemos/src/com/example/android/apis/gadget/ExampleGadgetProvider.java b/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetProvider.java similarity index 58% rename from samples/ApiDemos/src/com/example/android/apis/gadget/ExampleGadgetProvider.java rename to samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetProvider.java index eb1dab311..6977d3e6e 100644 --- a/samples/ApiDemos/src/com/example/android/apis/gadget/ExampleGadgetProvider.java +++ b/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetProvider.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.example.android.apis.gadget; +package com.example.android.apis.appwidget; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProvider; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; -import android.gadget.GadgetManager; -import android.gadget.GadgetProvider; import android.os.SystemClock; import android.util.Log; import android.widget.RemoteViews; @@ -33,89 +33,89 @@ import java.util.ArrayList; import com.example.android.apis.R; /** - * A gadget provider. We have a string that we pull from a preference in order to show - * the configuration settings and the current time when the gadget was updated. We also + * A widget provider. We have a string that we pull from a preference in order to show + * the configuration settings and the current time when the widget was updated. We also * register a BroadcastReceiver for time-changed and timezone-changed broadcasts, and * update then too. * *

See also the following files: *

    - *
  • ExampleGadgetConfigure.java
  • + *
  • ExampleAppWidgetConfigure.java
  • *
  • ExampleBroadcastReceiver.java
  • - *
  • res/layout/gadget_configure.xml
  • - *
  • res/layout/gadget_provider.xml
  • - *
  • res/xml/gadget_provider.xml
  • + *
  • res/layout/appwidget_configure.xml
  • + *
  • res/layout/appwidget_provider.xml
  • + *
  • res/xml/appwidget_provider.xml
  • *
*/ -public class ExampleGadgetProvider extends GadgetProvider { +public class ExampleAppWidgetProvider extends AppWidgetProvider { // log tag - private static final String TAG = "ExampleGadgetProvider"; + private static final String TAG = "ExampleAppWidgetProvider"; - public void onUpdate(Context context, GadgetManager gadgetManager, int[] gadgetIds) { + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { Log.d(TAG, "onUpdate"); - // For each gadget that needs an update, get the text that we should display: + // For each widget that needs an update, get the text that we should display: // - Create a RemoteViews object for it // - Set the text in the RemoteViews object - // - Tell the GadgetManager to show that views object for the gadget. - final int N = gadgetIds.length; + // - Tell the AppWidgetManager to show that views object for the widget. + final int N = appWidgetIds.length; for (int i=0; i - - + + array = new ArrayList(2); array.add(project); - AndroidManifestHelper helper = new AndroidManifestHelper(project); - IFile manifest = helper.getManifestIFile(); + IFile manifest = AndroidManifestParser.getManifest(project); if (manifest != null) { array.add(manifest); } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DeviceChooserDialog.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DeviceChooserDialog.java index 275addfbd..a960bda0d 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DeviceChooserDialog.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/DeviceChooserDialog.java @@ -36,12 +36,8 @@ import com.android.sdkuilib.AvdSelector; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.preference.IPreferenceStore; -import org.eclipse.jface.viewers.DoubleClickEvent; -import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ILabelProviderListener; -import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredContentProvider; -import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; @@ -62,6 +58,12 @@ import org.eclipse.swt.widgets.Table; import java.util.ArrayList; +/** + * A dialog that lets the user choose a device to deploy an application. + * The user can either choose an exiting running device (including running emulators) + * or start a new emulator using an Android Virtual Device configuration that matches + * the current project. + */ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener { private final static int ICON_WIDTH = 16; @@ -373,15 +375,27 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener mViewer.setContentProvider(new ContentProvider()); mViewer.setLabelProvider(new LabelProvider()); mViewer.setInput(AndroidDebugBridge.getBridge()); - mViewer.addDoubleClickListener(new IDoubleClickListener() { - public void doubleClick(DoubleClickEvent event) { - ISelection selection = event.getSelection(); - if (selection instanceof IStructuredSelection) { - IStructuredSelection structuredSelection = (IStructuredSelection)selection; - Object object = structuredSelection.getFirstElement(); - if (object instanceof Device) { - mResponse.setDeviceToUse((Device)object); - } + + mDeviceTable.addSelectionListener(new SelectionAdapter() { + /** + * Handles single-click selection on the device selector. + * {@inheritDoc} + */ + @Override + public void widgetSelected(SelectionEvent e) { + handleDeviceSelection(); + } + + /** + * Handles double-click selection on the device selector. + * Note that the single-click handler will probably already have been called. + * {@inheritDoc} + */ + @Override + public void widgetDefaultSelected(SelectionEvent e) { + handleDeviceSelection(); + if (isOkButtonEnabled()) { + okPressed(); } } }); @@ -397,18 +411,14 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener layout.marginLeft = 30; offsetComp.setLayout(layout); - mPreferredAvdSelector = new AvdSelector(offsetComp, getNonRunningAvds(), mProjectTarget, - false /*allowMultipleSelection*/); + mPreferredAvdSelector = new AvdSelector(offsetComp, getNonRunningAvds(), mProjectTarget); mPreferredAvdSelector.setTableHeightHint(100); mPreferredAvdSelector.setEnabled(false); - mDeviceTable.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - handleDeviceSelection(); - } - }); - mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() { + /** + * Handles single-click selection on the AVD selector. + * {@inheritDoc} + */ @Override public void widgetSelected(SelectionEvent e) { if (mDisableAvdSelectionChange == false) { @@ -416,6 +426,22 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener enableOkButton(); } } + + /** + * Handles double-click selection on the AVD selector. + * + * Note that the single-click handler will probably already have been called + * but the selected item can have changed in between. + * + * {@inheritDoc} + */ + @Override + public void widgetDefaultSelected(SelectionEvent e) { + widgetSelected(e); + if (isOkButtonEnabled()) { + okPressed(); + } + } }); AndroidDebugBridge.addDeviceChangeListener(this); @@ -586,6 +612,14 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener okButton.setEnabled(mResponse.getAvdToLaunch() != null); } } + + /** + * Returns true if the ok button is enabled. + */ + private boolean isOkButtonEnabled() { + Button okButton = getButton(IDialogConstants.OK_ID); + return okButton.isEnabled(); + } /** * Executes the {@link Runnable} in the UI thread. diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java index 5b4cdbb6f..b898f63c5 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/launch/EmulatorConfigTab.java @@ -178,8 +178,7 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab { mPreferredAvdLabel = new Label(offsetComp, SWT.NONE); mPreferredAvdLabel.setText("Select a preferred Android Virtual Device:"); AvdInfo[] avds = new AvdInfo[0]; - mPreferredAvdSelector = new AvdSelector(offsetComp, avds, - false /*allowMultipleSelection*/); + mPreferredAvdSelector = new AvdSelector(offsetComp, avds); mPreferredAvdSelector.setTableHeightHint(100); mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() { @Override diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java index b1f8ffc03..fd0c045d8 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/ProjectHelper.java @@ -19,9 +19,9 @@ package com.android.ide.eclipse.adt.project; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.project.internal.AndroidClasspathContainerInitializer; import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.common.project.AndroidManifestHelper; import com.android.ide.eclipse.common.project.AndroidManifestParser; +import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; @@ -389,18 +389,16 @@ public final class ProjectHelper { continue; } - AndroidManifestHelper androidManifest = new AndroidManifestHelper(p); - // check that there is indeed a manifest file. - if (androidManifest.getManifestIFile() == null) { + IFile manifestFile = AndroidManifestParser.getManifest(p); + if (manifestFile == null) { // no file? skip this project. continue; } AndroidManifestParser parser = null; try { - parser = AndroidManifestParser.parseForData( - androidManifest.getManifestIFile()); + parser = AndroidManifestParser.parseForData(manifestFile); } catch (CoreException e) { // skip this project. continue; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetData.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetData.java index a8852e7de..34391c236 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetData.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetData.java @@ -44,7 +44,7 @@ public class AndroidTargetData { public final static int DESCRIPTOR_RESOURCES = 5; public final static int DESCRIPTOR_SEARCHABLE = 6; public final static int DESCRIPTOR_PREFERENCES = 7; - public final static int DESCRIPTOR_GADGET_PROVIDER = 8; + public final static int DESCRIPTOR_APPWIDGET_PROVIDER = 8; public final static class LayoutBridge { /** Link to the layout bridge */ @@ -158,8 +158,8 @@ public class AndroidTargetData { return ResourcesDescriptors.getInstance(); case DESCRIPTOR_PREFERENCES: return mXmlDescriptors.getPreferencesProvider(); - case DESCRIPTOR_GADGET_PROVIDER: - return mXmlDescriptors.getGadgetProvider(); + case DESCRIPTOR_APPWIDGET_PROVIDER: + return mXmlDescriptors.getAppWidgetProvider(); case DESCRIPTOR_SEARCHABLE: return mXmlDescriptors.getSearchableProvider(); default : diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java index 04baeba92..67eec78b9 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java @@ -203,9 +203,9 @@ public final class AndroidTargetParser { attrsManifestXmlParser); Map> enumValueMap = attrsXmlParser.getEnumFlagValues(); - Map xmlGadgetMap = null; + Map xmlAppWidgetMap = null; if (mAndroidTarget.getApiVersionNumber() >= 3) { - xmlGadgetMap = collectGadgetDefinitions(attrsXmlParser); + xmlAppWidgetMap = collectAppWidgetDefinitions(attrsXmlParser); } if (progress.isCanceled()) { @@ -241,7 +241,7 @@ public final class AndroidTargetParser { XmlDescriptors xmlDescriptors = new XmlDescriptors(); xmlDescriptors.updateDescriptors( xmlSearchableMap, - xmlGadgetMap, + xmlAppWidgetMap, preferencesInfo, preferenceGroupsInfo); progress.worked(1); @@ -611,23 +611,23 @@ public final class AndroidTargetParser { } /** - * Collects all gadgetProviderInfo definition information from the attrs.xml and returns it. + * Collects all appWidgetProviderInfo definition information from the attrs.xml and returns it. * * @param attrsXmlParser The parser of the attrs.xml file */ - private Map collectGadgetDefinitions( + private Map collectAppWidgetDefinitions( AttrsXmlParser attrsXmlParser) { Map map = attrsXmlParser.getDeclareStyleableList(); Map map2 = new HashMap(); - for (String key : new String[] { "GadgetProviderInfo" }) { //$NON-NLS-1$ + for (String key : new String[] { "AppWidgetProviderInfo" }) { //$NON-NLS-1$ if (map.containsKey(key)) { map2.put(key, map.get(key)); } else { AdtPlugin.log(IStatus.WARNING, - "Gadget declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ + "AppWidget declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ key, attrsXmlParser.getOsAttrsXmlPath()); AdtPlugin.printErrorToConsole("Android Framework Parser", - String.format("Gadget declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ + String.format("AppWidget declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ key, attrsXmlParser.getOsAttrsXmlPath())); } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java index 33ec2bcff..6c4f4ba57 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectCreationPage.java @@ -24,7 +24,7 @@ package com.android.ide.eclipse.adt.wizards.newproject; import com.android.ide.eclipse.adt.sdk.Sdk; import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.common.project.AndroidManifestHelper; +import com.android.ide.eclipse.common.project.AndroidManifestParser; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkConstants; import com.android.sdklib.project.ProjectProperties; @@ -36,6 +36,7 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; @@ -821,26 +822,41 @@ public class NewProjectCreationPage extends WizardPage { Path path = new Path(f.getPath()); String osPath = path.append(AndroidConstants.FN_ANDROID_MANIFEST).toOSString(); - AndroidManifestHelper manifest = new AndroidManifestHelper(osPath); - if (!manifest.exists()) { + + AndroidManifestParser manifestData = null; + try { + manifestData = AndroidManifestParser.parseForData(osPath); + } catch (CoreException e1) { + // ignore any parsing issue + } + if (manifestData == null) { return; } String packageName = null; String activityName = null; - String minSdkVersion = null; + int minSdkVersion = 0; // 0 means no minSdkVersion provided in the manifest try { - packageName = manifest.getPackageName(); - activityName = manifest.getActivityName(1); - minSdkVersion = manifest.getMinSdkVersion(); + packageName = manifestData.getPackage(); + minSdkVersion = manifestData.getApiLevelRequirement(); + + // try to get the first launcher activity. If none, just take the first activity. + activityName = manifestData.getLauncherActivity(); + if (activityName == null) { + String[] activities = manifestData.getActivities(); + if (activities != null && activities.length > 0) { + activityName = activities[0]; + } + } } catch (Exception e) { // ignore exceptions } - if (packageName != null && packageName.length() > 0) { mPackageNameField.setText(packageName); } + + activityName = AndroidManifestParser.extractActivityName(activityName, packageName); if (activityName != null && activityName.length() > 0) { mInternalActivityNameUpdate = true; @@ -917,12 +933,10 @@ public class NewProjectCreationPage extends WizardPage { } } - if (!foundTarget && minSdkVersion != null) { + if (!foundTarget && minSdkVersion > 0) { try { - int sdkVersion = Integer.parseInt(minSdkVersion); - for (IAndroidTarget target : mSdkTargetSelector.getTargets()) { - if (target.getApiVersionNumber() == sdkVersion) { + if (target.getApiVersionNumber() == minSdkVersion) { mSdkTargetSelector.setSelection(target); foundTarget = true; break; @@ -945,7 +959,8 @@ public class NewProjectCreationPage extends WizardPage { if (!foundTarget) { mInternalMinSdkVersionUpdate = true; - mMinSdkVersionField.setText(minSdkVersion == null ? "" : minSdkVersion); //$NON-NLS-1$ + mMinSdkVersionField.setText( + minSdkVersion <= 0 ? "" : Integer.toString(minSdkVersion)); //$NON-NLS-1$ mInternalMinSdkVersionUpdate = false; } } @@ -1072,8 +1087,8 @@ public class NewProjectCreationPage extends WizardPage { // Check there's an android manifest in the directory String osPath = path.append(AndroidConstants.FN_ANDROID_MANIFEST).toOSString(); - AndroidManifestHelper manifest = new AndroidManifestHelper(osPath); - if (!manifest.exists()) { + File manifestFile = new File(osPath); + if (!manifestFile.isFile()) { return setStatus( String.format("File %1$s not found in %2$s.", AndroidConstants.FN_ANDROID_MANIFEST, f.getName()), @@ -1081,15 +1096,24 @@ public class NewProjectCreationPage extends WizardPage { } // Parse it and check the important fields. - String packageName = manifest.getPackageName(); + AndroidManifestParser manifestData; + try { + manifestData = AndroidManifestParser.parseForData(osPath); + } catch (CoreException e) { + return setStatus( + String.format("File %1$s could not be parsed.", osPath), + MSG_ERROR); + } + + String packageName = manifestData.getPackage(); if (packageName == null || packageName.length() == 0) { return setStatus( String.format("No package name defined in %1$s.", osPath), MSG_ERROR); } - String activityName = manifest.getActivityName(1); - if (activityName == null || activityName.length() == 0) { + String[] activities = manifestData.getActivities(); + if (activities == null || activities.length == 0) { // This is acceptable now as long as no activity needs to be created if (isCreateActivity()) { return setStatus( @@ -1097,7 +1121,7 @@ public class NewProjectCreationPage extends WizardPage { MSG_ERROR); } } - + // If there's already a .project, tell the user to use import instead. if (path.append(".project").toFile().exists()) { //$NON-NLS-1$ return setStatus("An Eclipse project already exists in this directory. Consider using File > Import > Existing Project instead.", diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestHelper.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestHelper.java deleted file mode 100644 index cd238d2bf..000000000 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestHelper.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (C) 2007 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.common.project; - -import com.android.ide.eclipse.common.AndroidConstants; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.runtime.CoreException; -import org.xml.sax.InputSource; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; - -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathExpressionException; - -/** - * Utility class that manages the AndroidManifest.xml file. - *

- * All the get method work by XPath. Repeated calls to those may warrant using - * {@link AndroidManifestParser} instead. - */ -public class AndroidManifestHelper { - private IFile mManifestIFile; - private File mManifestFile; - private XPath mXPath; - - /** - * Creates an AndroidManifest based on an existing Eclipse {@link IProject} object. - *

- * Use {@link #exists()} to check if the manifest file really exists in the project. - * - * @param project The project to search for the manifest. - */ - public AndroidManifestHelper(IProject project) { - mXPath = AndroidXPathFactory.newXPath(); - mManifestIFile = getManifest(project); - } - - /** - * Creates an AndroidManifest based on a file path. - *

- * Use {@link #exists()} to check if the manifest file really exists. - * - * @param osManifestFilePath the os path to the AndroidManifest.xml file. - */ - public AndroidManifestHelper(String osManifestFilePath) { - mXPath = AndroidXPathFactory.newXPath(); - mManifestFile = new File(osManifestFilePath); - } - - - /** - * Returns the underlying {@link IFile} for the android manifest XML file, if found in the - * given Eclipse project. - * - * Always return null if the constructor that takes an {@link IProject} was NOT called. - * - * @return The IFile for the androidManifest.xml or null if no such file could be found. - */ - public IFile getManifestIFile() { - return mManifestIFile; - } - - /** - * Returns the underlying {@link File} for the android manifest XML file. - */ - public File getManifestFile() { - if (mManifestIFile != null) { - return mManifestIFile.getLocation().toFile(); - } - - return mManifestFile; - } - - /** - * Returns the package name defined in the manifest file. - * - * @return A String object with the package or null if any error happened. - */ - public String getPackageName() { - try { - return mXPath.evaluate("/manifest/@package", getSource()); //$NON-NLS-1$ - } catch (XPathExpressionException e1) { - // If the XPath failed to evaluate, we'll return null. - } catch (Exception e) { - // if this happens this is due to the resource being out of sync. - // so we must refresh it and do it again - - // for any other kind of exception we must return null as well; - } - - return null; - } - - /** - * Returns the minSdkVersion defined in the manifest file. - * - * @return A String object with the package or null if any error happened. - */ - public String getMinSdkVersion() { - try { - return mXPath.evaluate("/manifest/uses-sdk/@" //$NON-NLS-1$ - + AndroidXPathFactory.DEFAULT_NS_PREFIX - + ":minSdkVersion", getSource()); //$NON-NLS-1$ - } catch (XPathExpressionException e1) { - // If the XPath failed to evaluate, we'll return null. - } catch (Exception e) { - // if this happens this is due to the resource being out of sync. - // so we must refresh it and do it again - - // for any other kind of exception we must return null as well; - } - - return null; - } - /** - * Returns the i-th activity defined in the manifest file. - * - * @param index The 1-based index of the activity to return. - * @return A String object with the activity or null if any error happened. - */ - public String getActivityName(int index) { - try { - return mXPath.evaluate("/manifest/application/activity[" //$NON-NLS-1$ - + index - + "]/@" //$NON-NLS-1$ - + AndroidXPathFactory.DEFAULT_NS_PREFIX +":name", //$NON-NLS-1$ - getSource()); - } catch (XPathExpressionException e1) { - // If the XPath failed to evaluate, we'll return null. - } catch (Exception e) { - // if this happens this is due to the resource being out of sync. - // so we must refresh it and do it again - - // for any other kind of exception we must return null as well; - } - return null; - } - - /** - * Returns an IFile object representing the manifest for the specified - * project. - * - * @param project The project containing the manifest file. - * @return An IFile object pointing to the manifest or null if the manifest - * is missing. - */ - public static IFile getManifest(IProject project) { - IResource r = project.findMember(AndroidConstants.WS_SEP - + AndroidConstants.FN_ANDROID_MANIFEST); - - if (r == null || r.exists() == false || (r instanceof IFile) == false) { - return null; - } - return (IFile) r; - } - - /** - * Combines a java package, with a class value from the manifest to make a fully qualified - * class name - * @param javaPackage the java package from the manifest. - * @param className the class name from the manifest. - * @return the fully qualified class name. - */ - public static String combinePackageAndClassName(String javaPackage, String className) { - if (className == null || className.length() == 0) { - return javaPackage; - } - if (javaPackage == null || javaPackage.length() == 0) { - return className; - } - - // the class name can be a subpackage (starts with a '.' - // char), a simple class name (no dot), or a full java package - boolean startWithDot = (className.charAt(0) == '.'); - boolean hasDot = (className.indexOf('.') != -1); - if (startWithDot || hasDot == false) { - - // add the concatenation of the package and class name - if (startWithDot) { - return javaPackage + className; - } else { - return javaPackage + '.' + className; - } - } else { - // just add the class as it should be a fully qualified java name. - return className; - } - } - - - - /** - * Returns true either if an androidManifest.xml file was found in the project - * or if the given file path exists. - */ - public boolean exists() { - if (mManifestIFile != null) { - return mManifestIFile.exists(); - } else if (mManifestFile != null) { - return mManifestFile.exists(); - } - - return false; - } - - /** - * Returns an InputSource for XPath. - * - * @throws FileNotFoundException if file does not exist. - * @throws CoreException if the {@link IFile} does not exist. - */ - private InputSource getSource() throws FileNotFoundException, CoreException { - if (mManifestIFile != null) { - return new InputSource(mManifestIFile.getContents()); - } else if (mManifestFile != null) { - return new InputSource(new FileReader(mManifestFile)); - } - - return null; - } - -} diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java index 850c59d71..b2817ff0d 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/AndroidManifestParser.java @@ -22,6 +22,8 @@ import com.android.sdklib.SdkConstants; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.IJavaProject; import org.xml.sax.Attributes; @@ -30,6 +32,8 @@ import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; +import java.io.File; +import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Set; @@ -56,6 +60,8 @@ public class AndroidManifestParser { private final static String NODE_ACTION = "action"; //$NON-NLS-1$ private final static String NODE_CATEGORY = "category"; //$NON-NLS-1$ private final static String NODE_USES_SDK = "uses-sdk"; //$NON-NLS-1$ + private final static String NODE_INSTRUMENTATION = "instrumentation"; //$NON-NLS-1$ + private final static String NODE_USES_LIBRARY = "uses-library"; //$NON-NLS-1$ private final static int LEVEL_MANIFEST = 0; private final static int LEVEL_APPLICATION = 1; @@ -66,6 +72,12 @@ public class AndroidManifestParser { private final static String ACTION_MAIN = "android.intent.action.MAIN"; //$NON-NLS-1$ private final static String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER"; //$NON-NLS-1$ + /** + * XML error & data handler used when parsing the AndroidManifest.xml file. + *

+ * This serves both as an {@link XmlErrorHandler} to report errors and as a data repository + * to collect data from the manifest. + */ private static class ManifestHandler extends XmlErrorHandler { //--- data read from the parsing @@ -82,6 +94,10 @@ public class AndroidManifestParser { private Boolean mDebuggable = null; /** API level requirement. if 0 the attribute was not present. */ private int mApiLevelRequirement = 0; + /** List of all instrumentations declared by the manifest */ + private final ArrayList mInstrumentations = new ArrayList(); + /** List of all libraries in use declared by the manifest */ + private final ArrayList mLibraries = new ArrayList(); //--- temporary data/flags used during parsing private IJavaProject mJavaProject; @@ -95,12 +111,13 @@ public class AndroidManifestParser { private Locator mLocator; /** - * - * @param manifestFile - * @param errorListener - * @param gatherData - * @param javaProject - * @param markErrors + * Creates a new {@link ManifestHandler}, which is also an {@link XmlErrorHandler}. + * + * @param manifestFile The manifest file being parsed. Can be null. + * @param errorListener An optional error listener. + * @param gatherData True if data should be gathered. + * @param javaProject The java project holding the manifest file. Can be null. + * @param markErrors True if errors should be marked as Eclipse Markers on the resource. */ ManifestHandler(IFile manifestFile, XmlErrorListener errorListener, boolean gatherData, IJavaProject javaProject, boolean markErrors) { @@ -160,6 +177,23 @@ public class AndroidManifestParser { return mApiLevelRequirement; } + /** + * Returns the list of instrumentations found in the manifest. + * @return An array of instrumentation names, or empty if no instrumentations were + * found. + */ + String[] getInstrumentations() { + return mInstrumentations.toArray(new String[mInstrumentations.size()]); + } + + /** + * Returns the list of libraries in use found in the manifest. + * @return An array of library names, or empty if no libraries were found. + */ + String[] getUsesLibraries() { + return mLibraries.toArray(new String[mLibraries.size()]); + } + /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#setDocumentLocator(org.xml.sax.Locator) */ @@ -217,7 +251,13 @@ public class AndroidManifestParser { } catch (NumberFormatException e) { handleError(e, -1 /* lineNumber */); } - } + } else if (NODE_INSTRUMENTATION.equals(localName)) { + value = getAttributeValue(attributes, ATTRIBUTE_NAME, + true /* hasNamespace */); + if (value != null) { + mInstrumentations.add(value); + } + } break; case LEVEL_ACTIVITY: if (NODE_ACTIVITY.equals(localName)) { @@ -232,7 +272,13 @@ public class AndroidManifestParser { } else if (NODE_PROVIDER.equals(localName)) { processNode(attributes, AndroidConstants.CLASS_CONTENTPROVIDER); mValidLevel++; - } + } else if (NODE_USES_LIBRARY.equals(localName)) { + value = getAttributeValue(attributes, ATTRIBUTE_NAME, + true /* hasNamespace */); + if (value != null) { + mLibraries.add(value); + } + } break; case LEVEL_INTENT_FILTER: // only process this level if we are in an activity @@ -355,8 +401,7 @@ public class AndroidManifestParser { String activityName = getAttributeValue(attributes, ATTRIBUTE_NAME, true /* hasNamespace */); if (activityName != null) { - mCurrentActivity = AndroidManifestHelper.combinePackageAndClassName(mPackage, - activityName); + mCurrentActivity = combinePackageAndClassName(mPackage, activityName); mActivities.add(mCurrentActivity); if (mMarkErrors) { @@ -387,8 +432,7 @@ public class AndroidManifestParser { String serviceName = getAttributeValue(attributes, ATTRIBUTE_NAME, true /* hasNamespace */); if (serviceName != null) { - serviceName = AndroidManifestHelper.combinePackageAndClassName(mPackage, - serviceName); + serviceName = combinePackageAndClassName(mPackage, serviceName); if (mMarkErrors) { checkClass(serviceName, superClassName, false /* testVisibility */); @@ -412,6 +456,9 @@ public class AndroidManifestParser { * the class or of its constructors. */ private void checkClass(String className, String superClassName, boolean testVisibility) { + if (mJavaProject == null) { + return; + } // we need to check the validity of the activity. String result = BaseProjectHelper.testClassForManifest(mJavaProject, className, superClassName, testVisibility); @@ -477,6 +524,8 @@ public class AndroidManifestParser { private final String[] mProcesses; private final Boolean mDebuggable; private final int mApiLevelRequirement; + private final String[] mInstrumentations; + private final String[] mLibraries; static { sParserFactory = SAXParserFactory.newInstance(); @@ -484,8 +533,12 @@ public class AndroidManifestParser { } /** - * Parses the Android Manifest, and returns an object containing - * the result of the parsing. + * Parses the Android Manifest, and returns an object containing the result of the parsing. + *

+ * This method is useful to parse a specific {@link IFile} in a Java project. + *

+ * If you only want to gather data, consider {@link #parseForData(IFile)} instead. + * * @param javaProject The java project. * @param manifestFile the {@link IFile} representing the manifest file. * @param errorListener @@ -496,8 +549,12 @@ public class AndroidManifestParser { * @return an {@link AndroidManifestParser} or null if the parsing failed. * @throws CoreException */ - public static AndroidManifestParser parse(IJavaProject javaProject, IFile manifestFile, - XmlErrorListener errorListener, boolean gatherData, boolean markErrors) + public static AndroidManifestParser parse( + IJavaProject javaProject, + IFile manifestFile, + XmlErrorListener errorListener, + boolean gatherData, + boolean markErrors) throws CoreException { try { SAXParser parser = sParserFactory.newSAXParser(); @@ -512,7 +569,51 @@ public class AndroidManifestParser { return new AndroidManifestParser(manifestHandler.getPackage(), manifestHandler.getActivities(), manifestHandler.getLauncherActivity(), manifestHandler.getProcesses(), manifestHandler.getDebuggable(), - manifestHandler.getApiLevelRequirement()); + manifestHandler.getApiLevelRequirement(), manifestHandler.getInstrumentations(), + manifestHandler.getUsesLibraries()); + } catch (ParserConfigurationException e) { + } catch (SAXException e) { + } catch (IOException e) { + } finally { + } + + return null; + } + + /** + * Parses the Android Manifest, and returns an object containing the result of the parsing. + *

+ * This version parses a real {@link File} file given by an actual path, which is useful for + * parsing a file that is not part of an Eclipse Java project. + *

+ * It assumes errors cannot be marked on the file and that data gathering is enabled. + * + * @param manifestFile the manifest file to parse. + * @return an {@link AndroidManifestParser} or null if the parsing failed. + * @throws CoreException + */ + private static AndroidManifestParser parse(File manifestFile) + throws CoreException { + try { + SAXParser parser = sParserFactory.newSAXParser(); + + ManifestHandler manifestHandler = new ManifestHandler( + null, //manifestFile + null, //errorListener + true, //gatherData + null, //javaProject + false //markErrors + ); + + parser.parse(new InputSource(new FileReader(manifestFile)), manifestHandler); + + // get the result from the handler + + return new AndroidManifestParser(manifestHandler.getPackage(), + manifestHandler.getActivities(), manifestHandler.getLauncherActivity(), + manifestHandler.getProcesses(), manifestHandler.getDebuggable(), + manifestHandler.getApiLevelRequirement(), manifestHandler.getInstrumentations(), + manifestHandler.getUsesLibraries()); } catch (ParserConfigurationException e) { } catch (SAXException e) { } catch (IOException e) { @@ -535,13 +636,16 @@ public class AndroidManifestParser { * @return an {@link AndroidManifestParser} or null if the parsing failed. * @throws CoreException */ - public static AndroidManifestParser parse(IJavaProject javaProject, - XmlErrorListener errorListener, boolean gatherData, boolean markErrors) + public static AndroidManifestParser parse( + IJavaProject javaProject, + XmlErrorListener errorListener, + boolean gatherData, + boolean markErrors) throws CoreException { try { SAXParser parser = sParserFactory.newSAXParser(); - IFile manifestFile = AndroidManifestHelper.getManifest(javaProject.getProject()); + IFile manifestFile = getManifest(javaProject.getProject()); if (manifestFile != null) { ManifestHandler manifestHandler = new ManifestHandler(manifestFile, errorListener, gatherData, javaProject, markErrors); @@ -552,7 +656,8 @@ public class AndroidManifestParser { return new AndroidManifestParser(manifestHandler.getPackage(), manifestHandler.getActivities(), manifestHandler.getLauncherActivity(), manifestHandler.getProcesses(), manifestHandler.getDebuggable(), - manifestHandler.getApiLevelRequirement()); + manifestHandler.getApiLevelRequirement(), + manifestHandler.getInstrumentations(), manifestHandler.getUsesLibraries()); } } catch (ParserConfigurationException e) { } catch (SAXException e) { @@ -588,6 +693,18 @@ public class AndroidManifestParser { true /* gatherData */, false /* markErrors */); } + /** + * Parses the manifest file, and collects data. + * + * @param osManifestFilePath The OS path of the manifest file to parse. + * @return an {@link AndroidManifestParser} or null if the parsing failed. + * @throws CoreException + */ + public static AndroidManifestParser parseForData(String osManifestFilePath) + throws CoreException { + return parse(new File(osManifestFilePath)); + } + /** * Returns the package defined in the manifest, if found. * @return The package name or null if not found. @@ -633,6 +750,22 @@ public class AndroidManifestParser { public int getApiLevelRequirement() { return mApiLevelRequirement; } + + /** + * Returns the list of instrumentations found in the manifest. + * @return An array of fully qualified class names, or empty if no instrumentations were found. + */ + public String[] getInstrumentations() { + return mInstrumentations; + } + + /** + * Returns the list of libraries in use found in the manifest. + * @return An array of library names, or empty if no uses-library declarations were found. + */ + public String[] getUsesLibraries() { + return mLibraries; + } /** @@ -647,15 +780,92 @@ public class AndroidManifestParser { * @param processes the list of custom processes declared in the manifest. * @param debuggable the debuggable attribute, or null if not set. * @param apiLevelRequirement the minSdkVersion attribute value or 0 if not set. + * @param instrumentations the list of instrumentations parsed from the manifest. + * @param libraries the list of libraries in use parsed from the manifest. */ private AndroidManifestParser(String javaPackage, String[] activities, String launcherActivity, String[] processes, Boolean debuggable, - int apiLevelRequirement) { + int apiLevelRequirement, String[] instrumentations, String[] libraries) { mJavaPackage = javaPackage; mActivities = activities; mLauncherActivity = launcherActivity; mProcesses = processes; mDebuggable = debuggable; mApiLevelRequirement = apiLevelRequirement; + mInstrumentations = instrumentations; + mLibraries = libraries; + } + + /** + * Returns an IFile object representing the manifest for the specified + * project. + * + * @param project The project containing the manifest file. + * @return An IFile object pointing to the manifest or null if the manifest + * is missing. + */ + public static IFile getManifest(IProject project) { + IResource r = project.findMember(AndroidConstants.WS_SEP + + AndroidConstants.FN_ANDROID_MANIFEST); + + if (r == null || r.exists() == false || (r instanceof IFile) == false) { + return null; + } + return (IFile) r; + } + + /** + * Combines a java package, with a class value from the manifest to make a fully qualified + * class name + * @param javaPackage the java package from the manifest. + * @param className the class name from the manifest. + * @return the fully qualified class name. + */ + public static String combinePackageAndClassName(String javaPackage, String className) { + if (className == null || className.length() == 0) { + return javaPackage; + } + if (javaPackage == null || javaPackage.length() == 0) { + return className; + } + + // the class name can be a subpackage (starts with a '.' + // char), a simple class name (no dot), or a full java package + boolean startWithDot = (className.charAt(0) == '.'); + boolean hasDot = (className.indexOf('.') != -1); + if (startWithDot || hasDot == false) { + + // add the concatenation of the package and class name + if (startWithDot) { + return javaPackage + className; + } else { + return javaPackage + '.' + className; + } + } else { + // just add the class as it should be a fully qualified java name. + return className; + } + } + + /** + * Given a fully qualified activity name (e.g. com.foo.test.MyClass) and given a project + * package base name (e.g. com.foo), returns the relative activity name that would be used + * the "name" attribute of an "activity" element. + * + * @param fullActivityName a fully qualified activity class name, e.g. "com.foo.test.MyClass" + * @param packageName The project base package name, e.g. "com.foo" + * @return The relative activity name if it can be computed or the original fullActivityName. + */ + public static String extractActivityName(String fullActivityName, String packageName) { + if (packageName != null && fullActivityName != null) { + if (packageName.length() > 0 && fullActivityName.startsWith(packageName)) { + String name = fullActivityName.substring(packageName.length()); + if (name.length() > 0 && name.charAt(0) == '.') { + return name; + } + } + } + + return fullActivityName; } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/XmlErrorHandler.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/XmlErrorHandler.java index fda55c450..1810ad295 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/XmlErrorHandler.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/project/XmlErrorHandler.java @@ -86,8 +86,13 @@ public class XmlErrorHandler extends DefaultHandler { */ @Override public void warning(SAXParseException exception) throws SAXException { - BaseProjectHelper.addMarker(mFile, AndroidConstants.MARKER_XML, exception.getMessage(), - exception.getLineNumber(), IMarker.SEVERITY_WARNING); + if (mFile != null) { + BaseProjectHelper.addMarker(mFile, + AndroidConstants.MARKER_XML, + exception.getMessage(), + exception.getLineNumber(), + IMarker.SEVERITY_WARNING); + } } protected final IFile getFile() { @@ -104,12 +109,19 @@ public class XmlErrorHandler extends DefaultHandler { mErrorListener.errorFound(); } - if (lineNumber != -1) { - BaseProjectHelper.addMarker(mFile, AndroidConstants.MARKER_XML, exception.getMessage(), - lineNumber, IMarker.SEVERITY_ERROR); - } else { - BaseProjectHelper.addMarker(mFile, AndroidConstants.MARKER_XML, exception.getMessage(), - IMarker.SEVERITY_ERROR); + if (mFile != null) { + if (lineNumber != -1) { + BaseProjectHelper.addMarker(mFile, + AndroidConstants.MARKER_XML, + exception.getMessage(), + lineNumber, + IMarker.SEVERITY_ERROR); + } else { + BaseProjectHelper.addMarker(mFile, + AndroidConstants.MARKER_XML, + exception.getMessage(), + IMarker.SEVERITY_ERROR); + } } } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidContentAssist.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidContentAssist.java index 332ce6f83..0d0883ecb 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidContentAssist.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidContentAssist.java @@ -442,11 +442,15 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor { tooltip = ((TextAttributeDescriptor) choice).getTooltip(); } + // Get the namespace URI for the attribute. Note that some attributes + // do not have a namespace and thus return null here. String nsUri = ((AttributeDescriptor)choice).getNamespaceUri(); - nsPrefix = nsUriMap.get(nsUri); - if (nsPrefix == null) { - nsPrefix = lookupNamespacePrefix(currentNode, nsUri); - nsUriMap.put(nsUri, nsPrefix); + if (nsUri != null) { + nsPrefix = nsUriMap.get(nsUri); + if (nsPrefix == null) { + nsPrefix = lookupNamespacePrefix(currentNode, nsUri); + nsUriMap.put(nsUri, nsPrefix); + } } if (nsPrefix != null) { nsPrefix += ":"; //$NON-NLS-1$ diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/ProjectCallback.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/ProjectCallback.java index 81fd2ed0c..94ad87a11 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/ProjectCallback.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/ProjectCallback.java @@ -18,12 +18,14 @@ package com.android.ide.eclipse.editors.layout; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.common.project.AndroidManifestHelper; +import com.android.ide.eclipse.common.project.AndroidManifestParser; import com.android.ide.eclipse.editors.resources.manager.ProjectClassLoader; import com.android.ide.eclipse.editors.resources.manager.ProjectResources; import com.android.layoutlib.api.IProjectCallback; +import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; import java.lang.reflect.Constructor; import java.util.HashMap; @@ -83,17 +85,20 @@ public final class ProjectCallback implements IProjectCallback { } /** - * {@inheritDoc} - * * Returns the namespace for the project. The namespace contains a standard part + the * application package. + * + * @return The package namespace of the project or null in case of error. */ public String getNamespace() { if (mNamespace == null) { - AndroidManifestHelper manifest = new AndroidManifestHelper(mProject); - String javaPackage = manifest.getPackageName(); - - mNamespace = String.format(AndroidConstants.NS_CUSTOM_RESOURCES, javaPackage); + IFile manifestFile = AndroidManifestParser.getManifest(mProject); + try { + AndroidManifestParser data = AndroidManifestParser.parseForData(manifestFile); + String javaPackage = data.getPackage(); + mNamespace = String.format(AndroidConstants.NS_CUSTOM_RESOURCES, javaPackage); + } catch (CoreException e) { + } } return mNamespace; diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java index 7caa50f12..c3f4dd807 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/descriptors/LayoutDescriptors.java @@ -17,6 +17,8 @@ package com.android.ide.eclipse.editors.layout.descriptors; import com.android.ide.eclipse.common.AndroidConstants; +import com.android.ide.eclipse.common.resources.DeclareStyleableInfo; +import com.android.ide.eclipse.common.resources.ResourceType; import com.android.ide.eclipse.common.resources.ViewClassInfo; import com.android.ide.eclipse.common.resources.DeclareStyleableInfo.AttributeInfo; import com.android.ide.eclipse.common.resources.ViewClassInfo.LayoutParamsInfo; @@ -25,6 +27,7 @@ import com.android.ide.eclipse.editors.descriptors.DescriptorsUtils; import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor; import com.android.ide.eclipse.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.editors.descriptors.IDescriptorProvider; +import com.android.ide.eclipse.editors.descriptors.ReferenceAttributeDescriptor; import com.android.ide.eclipse.editors.descriptors.SeparatorAttributeDescriptor; import com.android.sdklib.SdkConstants; @@ -131,8 +134,23 @@ public final class LayoutDescriptors implements IDescriptorProvider { String xml_name = info.getShortClassName(); String tooltip = info.getJavaDoc(); - // Process all View attributes ArrayList attributes = new ArrayList(); + + // All views and groups have an implicit "style" attribute which is a reference. + AttributeInfo styleInfo = new DeclareStyleableInfo.AttributeInfo( + "style", //$NON-NLS-1$ xmlLocalName + new DeclareStyleableInfo.AttributeInfo.Format[] { + DeclareStyleableInfo.AttributeInfo.Format.REFERENCE + }); + styleInfo.setJavaDoc("A reference to a custom style"); //tooltip + DescriptorsUtils.appendAttribute(attributes, + "style", //$NON-NLS-1$ + null, //nsUri + styleInfo, + false, //required + null); // overrides + + // Process all View attributes DescriptorsUtils.appendAttributes(attributes, null, // elementName SdkConstants.NS_RESOURCES, @@ -155,7 +173,7 @@ public final class LayoutDescriptors implements IDescriptorProvider { null /* overrides */); } } - + // Process all LayoutParams attributes ArrayList layoutAttributes = new ArrayList(); LayoutParamsInfo layoutParams = info.getLayoutData(); diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiClassAttributeNode.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiClassAttributeNode.java index f886080f0..e32be86b0 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiClassAttributeNode.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/model/UiClassAttributeNode.java @@ -17,7 +17,7 @@ package com.android.ide.eclipse.editors.manifest.model; import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.common.project.AndroidManifestHelper; +import com.android.ide.eclipse.common.project.AndroidManifestParser; import com.android.ide.eclipse.common.project.BaseProjectHelper; import com.android.ide.eclipse.editors.AndroidEditor; import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor; @@ -251,8 +251,8 @@ public class UiClassAttributeNode extends UiTextAttributeNode { String javaPackage = getManifestPackage(); // build the fully qualified name of the class - String className = AndroidManifestHelper.combinePackageAndClassName(javaPackage, - textValue); + String className = AndroidManifestParser.combinePackageAndClassName( + javaPackage, textValue); // only test the vilibility for activities. boolean testVisibility = AndroidConstants.CLASS_ACTIVITY.equals( diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/CompiledResourcesMonitor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/CompiledResourcesMonitor.java index 455c825af..fa0930553 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/CompiledResourcesMonitor.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/resources/manager/CompiledResourcesMonitor.java @@ -18,7 +18,7 @@ package com.android.ide.eclipse.editors.resources.manager; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.common.AndroidConstants; -import com.android.ide.eclipse.common.project.AndroidManifestHelper; +import com.android.ide.eclipse.common.project.AndroidManifestParser; import com.android.ide.eclipse.common.resources.ResourceType; import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IFileListener; import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener; @@ -28,6 +28,7 @@ import org.eclipse.core.resources.IMarkerDelta; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -120,7 +121,14 @@ public final class CompiledResourcesMonitor implements IFileListener, IProjectLi if (projectResources != null) { // create the classname String className = getRClassName(project); - + if (className == null) { + // We need to abort. + AdtPlugin.log(IStatus.ERROR, + "loadAndParseRClass: failed to find manifest package for project %1$s", //$NON-NLS-1$ + project.getName()); + return; + } + // create a temporary class loader to load it. ProjectClassLoader loader = new ProjectClassLoader(null /* parentClassLoader */, project); @@ -199,13 +207,28 @@ public final class CompiledResourcesMonitor implements IFileListener, IProjectLi } return false; } - + + /** + * Returns the class name of the R class, based on the project's manifest's package. + * + * @return A class name (e.g. "my.app.R") or null if there's no valid package in the manifest. + */ private String getRClassName(IProject project) { - // create the classname - AndroidManifestHelper manifest = new AndroidManifestHelper(project); - String javaPackage = manifest.getPackageName(); - - return javaPackage + ".R"; //$NON-NLS-1$ + try { + IFile manifestFile = AndroidManifestParser.getManifest(project); + AndroidManifestParser data = AndroidManifestParser.parseForData(manifestFile); + String javaPackage = data.getPackage(); + return javaPackage + ".R"; //$NON-NLS-1$ + } catch (CoreException e) { + // This will typically happen either because the manifest file is not present + // and/or the workspace needs to be refreshed. + AdtPlugin.logAndPrintError(e, + "Android Resources", + "Failed to find the package of the AndroidManifest of project %1$s. Reason: %2$s", + project.getName(), + e.getMessage()); + return null; + } } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java index 578193854..e84c05123 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java @@ -222,10 +222,10 @@ class NewXmlFileCreationPage extends WizardPage { null, // default attributes 1 // target API level ), - new TypeInfo("Gadget Provider", // UI name - "An XML file that describes a gadget provider.", // tooltip + new TypeInfo("AppWidget Provider", // UI name + "An XML file that describes a widget provider.", // tooltip ResourceFolderType.XML, // folder type - AndroidTargetData.DESCRIPTOR_GADGET_PROVIDER, // root seed + AndroidTargetData.DESCRIPTOR_APPWIDGET_PROVIDER, // root seed null, // default root SdkConstants.NS_RESOURCES, // xmlns null, // default attributes @@ -1109,7 +1109,7 @@ class NewXmlFileCreationPage extends WizardPage { TypeInfo type = getSelectedType(); if (type.getTargetApiLevel() > currentApiLevel) { - error = "The API level of the selected type (e.g. gadget, etc.) is not " + + error = "The API level of the selected type (e.g. AppWidget, etc.) is not " + "compatible with the API level of the project."; } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/descriptors/XmlDescriptors.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/descriptors/XmlDescriptors.java index 7929b5aef..144b7ac26 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/descriptors/XmlDescriptors.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/descriptors/XmlDescriptors.java @@ -53,8 +53,8 @@ public final class XmlDescriptors implements IDescriptorProvider { /** The root document descriptor for preferences. */ private DocumentDescriptor mPrefDescriptor = new DocumentDescriptor("xml_doc", null /* children */); //$NON-NLS-1$ - /** The root document descriptor for gadget provider. */ - private DocumentDescriptor mGadgetDescriptor = new DocumentDescriptor("xml_doc", null /* children */); //$NON-NLS-1$ + /** The root document descriptor for widget provider. */ + private DocumentDescriptor mAppWidgetDescriptor = new DocumentDescriptor("xml_doc", null /* children */); //$NON-NLS-1$ /** @return the root descriptor for both searchable and preferences. */ public DocumentDescriptor getDescriptor() { @@ -75,9 +75,9 @@ public final class XmlDescriptors implements IDescriptorProvider { return mPrefDescriptor; } - /** @return the root descriptor for gadget providers. */ - public DocumentDescriptor getGadgetDescriptor() { - return mGadgetDescriptor; + /** @return the root descriptor for widget providers. */ + public DocumentDescriptor getAppWidgetDescriptor() { + return mAppWidgetDescriptor; } public IDescriptorProvider getSearchableProvider() { @@ -104,14 +104,14 @@ public final class XmlDescriptors implements IDescriptorProvider { }; } - public IDescriptorProvider getGadgetProvider() { + public IDescriptorProvider getAppWidgetProvider() { return new IDescriptorProvider() { public ElementDescriptor getDescriptor() { - return mGadgetDescriptor; + return mAppWidgetDescriptor; } public ElementDescriptor[] getRootElementDescriptors() { - return mGadgetDescriptor.getChildren(); + return mAppWidgetDescriptor.getChildren(); } }; } @@ -123,13 +123,13 @@ public final class XmlDescriptors implements IDescriptorProvider { * all at once. * * @param searchableStyleMap The map style=>attributes for from the attrs.xml file - * @param gadgetStyleMap The map style=>attributes for from the attrs.xml file + * @param appWidgetStyleMap The map style=>attributes for from the attrs.xml file * @param prefs The list of non-group preference descriptions * @param prefGroups The list of preference group descriptions */ public synchronized void updateDescriptors( Map searchableStyleMap, - Map gadgetStyleMap, + Map appWidgetStyleMap, ViewClassInfo[] prefs, ViewClassInfo[] prefGroups) { XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor( @@ -137,16 +137,16 @@ public final class XmlDescriptors implements IDescriptorProvider { SdkConstants.NS_RESOURCES); ElementDescriptor searchable = createSearchable(searchableStyleMap, xmlns); - ElementDescriptor gadget = createGadgetProviderInfo(gadgetStyleMap, xmlns); + ElementDescriptor appWidget = createAppWidgetProviderInfo(appWidgetStyleMap, xmlns); ElementDescriptor preferences = createPreference(prefs, prefGroups, xmlns); ArrayList list = new ArrayList(); if (searchable != null) { list.add(searchable); mSearchDescriptor.setChildren(new ElementDescriptor[]{ searchable }); } - if (gadget != null) { - list.add(gadget); - mGadgetDescriptor.setChildren(new ElementDescriptor[]{ gadget }); + if (appWidget != null) { + list.add(appWidget); + mAppWidgetDescriptor.setChildren(new ElementDescriptor[]{ appWidget }); } if (preferences != null) { list.add(preferences); @@ -190,25 +190,25 @@ public final class XmlDescriptors implements IDescriptorProvider { } /** - * Returns the new ElementDescriptor for + * Returns the new ElementDescriptor for */ - private ElementDescriptor createGadgetProviderInfo( - Map gadgetStyleMap, + private ElementDescriptor createAppWidgetProviderInfo( + Map appWidgetStyleMap, XmlnsAttributeDescriptor xmlns) { - if (gadgetStyleMap == null) { + if (appWidgetStyleMap == null) { return null; } - ElementDescriptor gadget = createElement(gadgetStyleMap, - "GadgetProviderInfo", //$NON-NLS-1$ styleName - "gadget-provider", //$NON-NLS-1$ xmlName - "Gadget Provider", // uiName + ElementDescriptor appWidget = createElement(appWidgetStyleMap, + "AppWidgetProviderInfo", //$NON-NLS-1$ styleName + "appwidget-provider", //$NON-NLS-1$ xmlName + "AppWidget Provider", // uiName null, // sdk url xmlns, // extraAttribute null, // childrenElements false /* mandatory */ ); - return gadget; + return appWidget; } /** diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/.classpath b/tools/eclipse/plugins/com.android.ide.eclipse.tests/.classpath index 40886832d..6f2a534bb 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.tests/.classpath +++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/.classpath @@ -5,6 +5,6 @@ - + diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestHelperTest.java b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestHelperTest.java deleted file mode 100644 index 66042648b..000000000 --- a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestHelperTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2007 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.common.project; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; - -import junit.framework.TestCase; - -public class AndroidManifestHelperTest extends TestCase { - private File mFile; - private AndroidManifestHelper mManifest; - - @Override - protected void setUp() throws Exception { - super.setUp(); - mFile = File.createTempFile("androidManifest", "xml"); //$NON-NLS-1$ //$NON-NLS-2$ - assertNotNull(mFile); - - FileWriter fw = new FileWriter(mFile); - fw.write("\n"); //$NON-NLS-1$ - fw.write("\n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \"\n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write(" \n"); //$NON-NLS-1$ - fw.write("\n"); //$NON-NLS-1$ - fw.flush(); - fw.close(); - - mManifest = new AndroidManifestHelper(mFile.getAbsolutePath()); - } - - @Override - protected void tearDown() throws Exception { - assertTrue(mFile.delete()); - super.tearDown(); - } - - public void testExists() { - assertTrue(mManifest.exists()); - } - - public void testNotExists() throws IOException { - File f = File.createTempFile("androidManifest2", "xml"); //$NON-NLS-1$ //$NON-NLS-2$ - assertTrue(f.delete()); - AndroidManifestHelper manifest = new AndroidManifestHelper(f.getAbsolutePath()); - assertFalse(manifest.exists()); - } - - public void testGetPackageName() { - assertEquals("com.android.testapp", mManifest.getPackageName()); - } - - public void testGetActivityName() { - assertEquals("", mManifest.getActivityName(0)); //$NON-NLS-1$ - assertEquals(".MainActivity", mManifest.getActivityName(1)); //$NON-NLS-1$ - assertEquals(".OptionsActivity", mManifest.getActivityName(2)); //$NON-NLS-1$ - assertEquals(".InfoActivity", mManifest.getActivityName(3)); //$NON-NLS-1$ - assertEquals("", mManifest.getActivityName(4)); //$NON-NLS-1$ - } - -} diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java new file mode 100644 index 000000000..516e448e6 --- /dev/null +++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/common/project/AndroidManifestParserTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2007 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.common.project; + +import junit.framework.TestCase; + +import com.android.ide.eclipse.mock.FileMock; + +/** + * Tests for {@link AndroidManifestParser} + */ +public class AndroidManifestParserTest extends TestCase { + private AndroidManifestParser mManifest; + + private static final String PACKAGE_NAME = "com.android.testapp"; //$NON-NLS-1$ + private static final String ACTIVITY_NAME = "com.android.testapp.MainActivity"; //$NON-NLS-1$ + private static final String LIBRARY_NAME = "android.test.runner"; //$NON-NLS-1$ + private static final String INSTRUMENTATION_NAME = "android.test.InstrumentationTestRunner"; //$NON-NLS-1$ + + @Override + protected void setUp() throws Exception { + super.setUp(); + + // create the test data + StringBuilder sb = new StringBuilder(); + sb.append("\n"); //$NON-NLS-1$ + sb.append("\n"); //$NON-NLS-1$ + sb.append(" \n"); //$NON-NLS-1$ + sb.append(" \n"); //$NON-NLS-1$ + sb.append(" \n"); //$NON-NLS-1$ + sb.append(" \n"); //$NON-NLS-1$ + sb.append(" \"\n"); //$NON-NLS-1$ + sb.append(" \n"); //$NON-NLS-1$ + sb.append(" \n"); //$NON-NLS-1$ + sb.append(" \n"); //$NON-NLS-1$ + sb.append(" \n"); //$NON-NLS-1$ + sb.append(" "); //$NON-NLS-1$ + sb.append(" \n"); + sb.append("\n"); //$NON-NLS-1$ + + FileMock mockFile = new FileMock("AndroidManifest.xml", sb.toString().getBytes()); + + mManifest = AndroidManifestParser.parseForData(mockFile); + assertNotNull(mManifest); + } + + public void testGetPackage() { + assertEquals("com.android.testapp", mManifest.getPackage()); + } + + public void testGetActivities() { + assertEquals(1, mManifest.getActivities().length); + assertEquals(ACTIVITY_NAME, mManifest.getActivities()[0]); + } + + public void testGetLauncherActivity() { + assertEquals(ACTIVITY_NAME, mManifest.getLauncherActivity()); + } + + public void testGetUsesLibraries() { + assertEquals(1, mManifest.getUsesLibraries().length); + assertEquals(LIBRARY_NAME, mManifest.getUsesLibraries()[0]); + } + + public void testGetInstrumentations() { + assertEquals(1, mManifest.getInstrumentations().length); + assertEquals(INSTRUMENTATION_NAME, mManifest.getInstrumentations()[0]); + } +} diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/FileMock.java b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/FileMock.java index 2220ed1cd..987ea9247 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/FileMock.java +++ b/tools/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/mock/FileMock.java @@ -37,6 +37,7 @@ import org.eclipse.core.runtime.jobs.ISchedulingRule; import sun.reflect.generics.reflectiveObjects.NotImplementedException; +import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.Reader; import java.net.URI; @@ -44,16 +45,28 @@ import java.util.Map; /** * Mock implementation of {@link IFile}. + * + * Optionally backed by an in-memory byte array + * *

Supported methods: *

    + *
  • getName()
  • + *
  • getContents()
  • + *
  • getContents(boolean force)
  • *
*/ public class FileMock implements IFile { private String mName; + private byte[] mContentData; public FileMock(String name) { + this(name, new byte[0]); + } + + public FileMock(String name, byte[] fileData) { mName = name; + mContentData = fileData; } // -------- MOCKED METHODS ---------------- @@ -62,6 +75,15 @@ public class FileMock implements IFile { return mName; } + public InputStream getContents() throws CoreException { + return new ByteArrayInputStream(mContentData); + } + + public InputStream getContents(boolean force) throws CoreException { + // ignore force + return getContents(); + } + // -------- UNIMPLEMENTED METHODS ---------------- public void appendContents(InputStream source, int updateFlags, IProgressMonitor monitor) @@ -115,14 +137,6 @@ public class FileMock implements IFile { throw new NotImplementedException(); } - public InputStream getContents() throws CoreException { - throw new NotImplementedException(); - } - - public InputStream getContents(boolean force) throws CoreException { - throw new NotImplementedException(); - } - public int getEncoding() throws CoreException { throw new NotImplementedException(); } @@ -139,7 +153,8 @@ public class FileMock implements IFile { throw new NotImplementedException(); } - public void move(IPath destination, boolean force, boolean keepHistory, IProgressMonitor monitor) + public void move(IPath destination, boolean force, boolean keepHistory, + IProgressMonitor monitor) throws CoreException { throw new NotImplementedException(); } @@ -229,7 +244,8 @@ public class FileMock implements IFile { throw new NotImplementedException(); } - public void deleteMarkers(String type, boolean includeSubtypes, int depth) throws CoreException { + public void deleteMarkers(String type, boolean includeSubtypes, int depth) + throws CoreException { throw new NotImplementedException(); } @@ -424,26 +440,26 @@ public class FileMock implements IFile { throw new NotImplementedException(); } - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") public Map getPersistentProperties() throws CoreException { throw new NotImplementedException(); - } + } - @SuppressWarnings("unchecked") + @SuppressWarnings("unchecked") public Map getSessionProperties() throws CoreException { throw new NotImplementedException(); - } + } - public boolean isDerived(int options) { + public boolean isDerived(int options) { throw new NotImplementedException(); - } + } - public boolean isHidden() { + public boolean isHidden() { throw new NotImplementedException(); - } + } - public void setHidden(boolean isHidden) throws CoreException { + public void setHidden(boolean isHidden) throws CoreException { throw new NotImplementedException(); - } - + } } + diff --git a/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ProfilesLoader.java b/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ProfilesLoader.java new file mode 100644 index 000000000..83b911342 --- /dev/null +++ b/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ProfilesLoader.java @@ -0,0 +1,77 @@ +/* + * 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.hierarchyviewer.scene; + +import com.android.ddmlib.Device; +import com.android.hierarchyviewer.device.Window; +import com.android.hierarchyviewer.device.DeviceBridge; + +import java.net.Socket; +import java.net.InetSocketAddress; +import java.io.BufferedWriter; +import java.io.OutputStreamWriter; +import java.io.IOException; +import java.io.BufferedReader; +import java.io.InputStreamReader; + +public class ProfilesLoader { + public static double[] loadProfiles(Device device, Window window, String params) { + Socket socket = null; + BufferedReader in = null; + BufferedWriter out = null; + + try { + socket = new Socket(); + socket.connect(new InetSocketAddress("127.0.0.1", + DeviceBridge.getDeviceLocalPort(device))); + + out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); + in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + + out.write("PROFILE " + window.encode() + " " + params); + out.newLine(); + out.flush(); + + String response = in.readLine(); + String[] data = response.split(" "); + + double[] profiles = new double[data.length]; + for (int i = 0; i < data.length; i++) { + profiles[i] = (Long.parseLong(data[i]) / 1000.0) / 1000.0; // convert to ms + } + return profiles; + } catch (IOException e) { + // Empty + } finally { + try { + if (out != null) { + out.close(); + } + if (in != null) { + in.close(); + } + if (socket != null) { + socket.close(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + return null; + } +} diff --git a/tools/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java b/tools/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java index 77ebb39e8..d530c3540 100644 --- a/tools/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java +++ b/tools/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java @@ -27,6 +27,7 @@ import com.android.hierarchyviewer.scene.ViewHierarchyScene; import com.android.hierarchyviewer.scene.ViewManager; import com.android.hierarchyviewer.scene.ViewNode; import com.android.hierarchyviewer.scene.WindowsLoader; +import com.android.hierarchyviewer.scene.ProfilesLoader; import com.android.hierarchyviewer.util.OS; import com.android.hierarchyviewer.util.WorkerThread; import com.android.hierarchyviewer.ui.action.ShowDevicesAction; @@ -43,6 +44,7 @@ import com.android.hierarchyviewer.ui.util.PngFileFilter; import com.android.hierarchyviewer.ui.util.IconLoader; import com.android.hierarchyviewer.ui.model.PropertiesTableModel; import com.android.hierarchyviewer.ui.model.ViewsTreeModel; +import com.android.hierarchyviewer.ui.model.ProfilesTableModel; import org.jdesktop.swingworker.SwingWorker; import org.netbeans.api.visual.graph.layout.TreeGraphLayout; import org.netbeans.api.visual.model.ObjectSceneEvent; @@ -123,6 +125,7 @@ public class Workspace extends JFrame { private JSplitPane sideSplitter; private JSplitPane mainSplitter; private JTable propertiesTable; + private JTable profilingTable; private JComponent pixelPerfectPanel; private JTree pixelPerfectTree; private ScreenViewer screenViewer; @@ -274,11 +277,32 @@ public class Workspace extends JFrame { JScrollPane tableScroller = new JScrollPane(propertiesTable); tableScroller.setBorder(null); + profilingTable = new JTable(); + profilingTable.setModel(new DefaultTableModel(new Object[][] { + { " " , " " }, { " " , " " }, { " " , " " } }, + new String[] { "Operation", "Duration (ms)" })); + profilingTable.setBorder(null); + profilingTable.getTableHeader().setBorder(null); + + JScrollPane firstTableScroller = new JScrollPane(profilingTable); + firstTableScroller.setBorder(null); + + setVisibleRowCount(profilingTable, 5); + firstTableScroller.setMinimumSize(profilingTable.getPreferredScrollableViewportSize()); + + JSplitPane tablesSplitter = new JSplitPane(); + tablesSplitter.setBorder(null); + tablesSplitter.setOrientation(JSplitPane.VERTICAL_SPLIT); + tablesSplitter.setResizeWeight(0); + tablesSplitter.setLeftComponent(firstTableScroller); + tablesSplitter.setBottomComponent(tableScroller); + tablesSplitter.setContinuousLayout(true); + sideSplitter = new JSplitPane(); sideSplitter.setBorder(null); sideSplitter.setOrientation(JSplitPane.VERTICAL_SPLIT); sideSplitter.setResizeWeight(0.5); - sideSplitter.setLeftComponent(tableScroller); + sideSplitter.setLeftComponent(tablesSplitter); sideSplitter.setBottomComponent(null); sideSplitter.setContinuousLayout(true); @@ -603,6 +627,22 @@ public class Workspace extends JFrame { propertiesTable.setModel(new PropertiesTableModel(node)); } + private void updateProfiles(double[] profiles) { + profilingTable.setModel(new ProfilesTableModel(profiles)); + setVisibleRowCount(profilingTable, profiles.length + 1); + } + + public static void setVisibleRowCount(JTable table, int rows) { + int height = 0; + for (int row = 0; row < rows; row++) { + height += table.getRowHeight(row); + } + + Dimension size = new Dimension(table.getPreferredScrollableViewportSize().width, height); + table.setPreferredScrollableViewportSize(size); + table.revalidate(); + } + private void showPixelPerfectTree() { if (pixelPerfectTree == null) { return; @@ -1134,22 +1174,24 @@ public class Workspace extends JFrame { } } - private class LoadGraphTask extends SwingWorker { + private class LoadGraphTask extends SwingWorker { public LoadGraphTask() { beginTask(); } @Override @WorkerThread - protected ViewHierarchyScene doInBackground() { + protected double[] doInBackground() { scene = ViewHierarchyLoader.loadScene(currentDevice, currentWindow); - return scene; + return ProfilesLoader.loadProfiles(currentDevice, currentWindow, + scene.getRoot().toString()); } @Override protected void done() { try { - createGraph(get()); + createGraph(scene); + updateProfiles(get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { diff --git a/tools/hierarchyviewer/src/com/android/hierarchyviewer/ui/model/ProfilesTableModel.java b/tools/hierarchyviewer/src/com/android/hierarchyviewer/ui/model/ProfilesTableModel.java new file mode 100644 index 000000000..fcbe6b57e --- /dev/null +++ b/tools/hierarchyviewer/src/com/android/hierarchyviewer/ui/model/ProfilesTableModel.java @@ -0,0 +1,68 @@ +/* + * 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.hierarchyviewer.ui.model; + +import javax.swing.table.DefaultTableModel; +import java.text.NumberFormat; + +public class ProfilesTableModel extends DefaultTableModel { + private static final String[] NAMES = { "measure", "layout", "draw" }; + + private final double[] profiles; + private final NumberFormat formatter; + + public ProfilesTableModel(double[] profiles) { + this.profiles = profiles; + formatter = NumberFormat.getNumberInstance(); + } + + @Override + public int getRowCount() { + return profiles == null ? 0 : profiles.length; + } + + @Override + public Object getValueAt(int row, int column) { + if (profiles == null) return ""; + + if (column == 0) { + return NAMES[row]; + } + + + return formatter.format(profiles[row]) + ""; + } + + @Override + public int getColumnCount() { + return 2; + } + + @Override + public String getColumnName(int column) { + return column == 0 ? "Operation" : "Duration (ms)"; + } + + @Override + public boolean isCellEditable(int arg0, int arg1) { + return false; + } + + @Override + public void setValueAt(Object arg0, int arg1, int arg2) { + } +} diff --git a/tools/runtest b/tools/runtest index 349b5a795..62809100f 100755 --- a/tools/runtest +++ b/tools/runtest @@ -113,7 +113,7 @@ knownTests=( # system-wide tests "framework frameworks/base/tests/FrameworkTest # com.android.frameworktest.AllTests com.android.frameworktest.tests #" - "android frameworks/base/tests/AndroidTests # AndroidTests com.android.unit_tests #" + "android frameworks/base/tests/AndroidTests com.android.unit_tests AndroidTests # #" "smoke frameworks/base/tests/SmokeTest com.android.smoketest # com.android.smoketest.tests #" "core frameworks/base/tests/CoreTests # android.core.CoreTests android.core #" "libcore frameworks/base/tests/CoreTests # android.core.JavaTests android.core #" diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java index 7b8fdbecf..65cbbe356 100644 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java @@ -303,8 +303,11 @@ public final class AvdManager { } if (NUMERIC_SKIN_SIZE.matcher(skinName).matches()) { - // Skin name is an actual screen resolution, no skin.path + // Skin name is an actual screen resolution. + // Set skin.name for display purposes in the AVD manager and + // set skin.path for use by the emulator. values.put(AVD_INI_SKIN_NAME, skinName); + values.put(AVD_INI_SKIN_PATH, skinName); } else { // get the path of the skin (relative to the SDK) // assume skin name is valid diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/ApkConfigWidget.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/ApkConfigWidget.java index 6bf1df308..825be93b1 100644 --- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/ApkConfigWidget.java +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/ApkConfigWidget.java @@ -41,7 +41,7 @@ import java.util.Set; * The APK Configuration widget is a table that is added to the given parent composite. *

* To use, create it using {@link #ApkConfigWidget(Composite)} then - * call {@link #fillTable(Map) to set the initial list of configurations. + * call {@link #fillTable(Map)} to set the initial list of configurations. */ public class ApkConfigWidget { private final static int INDEX_NAME = 0; diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java index 9d0b9285a..67c70a676 100644 --- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/AvdSelector.java @@ -36,21 +36,17 @@ import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; -import java.util.ArrayList; - /** * The AVD selector is a table that is added to the given parent composite. *

- * To use, create it using {@link #AvdSelector(Composite, AvdInfo[], boolean)} then + * To use, create it using {@link #AvdSelector(Composite, AvdInfo[])} then * call {@link #setSelection(AvdInfo)}, {@link #setSelectionListener(SelectionListener)} - * and finally use {@link #getFirstSelected()} or {@link #getAllSelected()} to retrieve the - * selection. + * and finally use {@link #getFirstSelected()} to retrieve the selection. */ public final class AvdSelector { private AvdInfo[] mAvds; - private final boolean mAllowMultipleSelection; private SelectionListener mSelectionListener; private Table mTable; private Label mDescription; @@ -63,11 +59,8 @@ public final class AvdSelector { * * @param parent The parent composite where the selector will be added. * @param avds The list of AVDs. This is not copied, the caller must not modify. - * @param allowMultipleSelection True if more than one SDK target can be selected at the same - * time. */ - public AvdSelector(Composite parent, AvdInfo[] avds, IAndroidTarget filter, - boolean allowMultipleSelection) { + public AvdSelector(Composite parent, AvdInfo[] avds, IAndroidTarget filter) { mAvds = avds; // Layout has 1 column @@ -76,7 +69,6 @@ public final class AvdSelector { group.setLayoutData(new GridData(GridData.FILL_BOTH)); group.setFont(parent.getFont()); - mAllowMultipleSelection = allowMultipleSelection; mTable = new Table(group, SWT.CHECK | SWT.FULL_SELECTION | SWT.SINGLE | SWT.BORDER); mTable.setHeaderVisible(true); mTable.setLinesVisible(false); @@ -112,11 +104,9 @@ public final class AvdSelector { * * @param parent The parent composite where the selector will be added. * @param avds The list of AVDs. This is not copied, the caller must not modify. - * @param allowMultipleSelection True if more than one SDK target can be selected at the same - * time. */ - public AvdSelector(Composite parent, AvdInfo[] avds, boolean allowMultipleSelection) { - this(parent, avds, null /* filter */, allowMultipleSelection); + public AvdSelector(Composite parent, AvdInfo[] avds) { + this(parent, avds, null /* filter */); } @@ -160,8 +150,7 @@ public final class AvdSelector { * The event's item contains a {@link TableItem}. * The {@link TableItem#getData()} contains an {@link IAndroidTarget}. *

- * It is recommended that the caller uses the {@link #getFirstSelected()} and - * {@link #getAllSelected()} methods instead. + * It is recommended that the caller uses the {@link #getFirstSelected()} method instead. * * @param selectionListener The new listener or null to remove it. */ @@ -201,28 +190,10 @@ public final class AvdSelector { return found; } - /** - * Returns all selected items. - * This is useful when the table is in multiple-selection mode. - * - * @see #getFirstSelected() - * @return An array of selected items. The list can be empty but not null. - */ - public AvdInfo[] getAllSelected() { - ArrayList list = new ArrayList(); - for (TableItem i : mTable.getItems()) { - if (i.getChecked()) { - list.add((IAndroidTarget) i.getData()); - } - } - return list.toArray(new AvdInfo[list.size()]); - } - /** * Returns the first selected item. * This is useful when the table is in single-selection mode. * - * @see #getAllSelected() * @return The first selected item or null. */ public AvdInfo getFirstSelected() { @@ -278,20 +249,11 @@ public final class AvdSelector { private void setupSelectionListener(final Table table) { // Add a selection listener that will check/uncheck items when they are double-clicked table.addSelectionListener(new SelectionListener() { - /** Default selection means double-click on "most" platforms */ - public void widgetDefaultSelected(SelectionEvent e) { - if (e.item instanceof TableItem) { - TableItem i = (TableItem) e.item; - i.setChecked(!i.getChecked()); - enforceSingleSelection(i); - updateDescription(i); - } - - if (mSelectionListener != null) { - mSelectionListener.widgetDefaultSelected(e); - } - } + /** + * Handles single-click selection on the table. + * {@inheritDoc} + */ public void widgetSelected(SelectionEvent e) { if (e.item instanceof TableItem) { TableItem i = (TableItem) e.item; @@ -305,11 +267,32 @@ public final class AvdSelector { } /** - * If we're not in multiple selection mode, uncheck all other - * items when this one is selected. + * Handles double-click selection on the table. + * Note that the single-click handler will probably already have been called. + * + * On double-click, always check the table item. + * + * {@inheritDoc} + */ + public void widgetDefaultSelected(SelectionEvent e) { + if (e.item instanceof TableItem) { + TableItem i = (TableItem) e.item; + i.setChecked(true); + enforceSingleSelection(i); + updateDescription(i); + } + + if (mSelectionListener != null) { + mSelectionListener.widgetDefaultSelected(e); + } + } + + /** + * To ensure single selection, uncheck all other items when this one is selected. + * This makes the chekboxes act as radio buttons. */ private void enforceSingleSelection(TableItem item) { - if (!mAllowMultipleSelection && item.getChecked()) { + if (item.getChecked()) { Table parentTable = item.getParent(); for (TableItem i2 : parentTable.getItems()) { if (i2 != item && i2.getChecked()) {