diff options
88 files changed, 2022 insertions, 1234 deletions
diff --git a/ddms/app/.classpath b/ddms/app/.classpath index 3f01de4..6caf176 100644 --- a/ddms/app/.classpath +++ b/ddms/app/.classpath @@ -3,9 +3,11 @@ <classpathentry kind="src" path="src"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/> + <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_OSGI"/> <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/> <classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/> <classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/> <classpathentry combineaccessrules="false" kind="src" path="/SdkStatsService"/> + <classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/swtmenubar.jar" sourcepath="/ANDROID_SRC/sdk/swtmenubar/src"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/ddms/app/Android.mk b/ddms/app/Android.mk index 6be32be..d1e4a52 100644 --- a/ddms/app/Android.mk +++ b/ddms/app/Android.mk @@ -17,6 +17,7 @@ LOCAL_JAVA_LIBRARIES := \ ddmlib \ ddmuilib \ swt \ + swtmenubar \ org.eclipse.jface_3.4.2.M20090107-0800 \ org.eclipse.equinox.common_3.4.0.v20080421-2006 \ org.eclipse.core.commands_3.4.0.I20080509-2000 diff --git a/ddms/app/README b/ddms/app/README index cc55ddd..42efb7b 100644 --- a/ddms/app/README +++ b/ddms/app/README @@ -1,11 +1,75 @@ -Using the Eclipse projects for ddms. +Using the Eclipse project DDMS +------------------------------ -ddms requires SWT to compile. +DDMS requires some external libraries to compile. +If you build DDMS using the makefile, you have nothing to configure. +However if you want to develop on DDMS using Eclipse, you need to +perform the following configuration. -SWT is available in the depot under //device/prebuild/<platform>/swt -Because the build path cannot contain relative path that are not inside the project directory, -the .classpath file references a user library called ANDROID_SWT. +------- +1- Projects required in Eclipse +------- -In order to compile the project, make a user library called ANDROID_SWT containing the jar -available at //device/prebuild/<platform>/swt.
\ No newline at end of file +To run DDMS from Eclipse, you need to import the following 5 projects: + + - sdk/androidpprefs: project AndroidPrefs + - sdk/sdkstats: project SdkStatsService + - sdk/ddms/app: project Ddms + - sdk/ddms/libs/ddmlib: project Ddmlib + - sdk/ddms/libs/ddmuilib: project Ddmuilib + + +------- +2- DDMS requires some SWT and OSGI JARs to compile. +------- + +SWT is available in the tree under prebuild/<platform>/swt + +Because the build path cannot contain relative path that are not inside +the project directory, the .classpath file references a user library +called ANDROID_SWT. +SWT depends on OSGI, so we'll also create an ANDROID_OSGI library for that. + +In order to compile the project: +- Open Preferences > Java > Build Path > User Libraries + +- Create a new user library named ANDROID_SWT +- Add the following 4 JAR files: + + - prebuilt/<platform>/swt/swt.jar + - prebuilt/common/eclipse/org.eclipse.core.commands_3.*.jar + - prebuilt/common/eclipse/org.eclipse.equinox.common_3.*.jar + - prebuilt/common/eclipse/org.eclipse.jface_3.*.jar + +- Create a new user library named ANDROID_OSGI +- Add the following JAR file: + + - prebuilt/common/osgi/osgi.jar + + +------- +3- DDMS also requires the compiled SwtMenuBar library. +------- + +Build the swtmenubar library: +$ cd $TOP (top of Android tree) +$ . build/envsetup.sh && lunch sdk-eng +$ sdk/eclipse/scripts/create_sdkman_symlinks.sh + +Define a classpath variable in Eclipse: +- Open Preferences > Java > Build Path > Classpath Variables +- Create a new classpath variable named ANDROID_OUT_FRAMEWORK +- Set its folder value to <Android tree>/out/host/<platform>/framework +- Create a new classpath variable named ANDROID_SRC +- Set its folder value to <Android tree> + +You might need to clean the ddms project (Project > Clean...) after +you add the new classpath variable, otherwise previous errors might not +go away automatically. + +The ANDROID_SRC part should be optional. It allows you to have access to +the SwtMenuBar generic parts from the Java editor. + +-- +EOF diff --git a/ddms/app/etc/manifest.txt b/ddms/app/etc/manifest.txt index e30c193..8c6ab23 100644 --- a/ddms/app/etc/manifest.txt +++ b/ddms/app/etc/manifest.txt @@ -1,3 +1,3 @@ Main-Class: com.android.ddms.Main -Class-Path: androidprefs.jar sdkstats.jar ddmlib.jar ddmuilib.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar jcommon-1.0.12.jar jfreechart-1.0.9.jar jfreechart-1.0.9-swt.jar osgi.jar +Class-Path: androidprefs.jar sdkstats.jar ddmlib.jar ddmuilib.jar swtmenubar.jar org.eclipse.jface_3.4.2.M20090107-0800.jar org.eclipse.equinox.common_3.4.0.v20080421-2006.jar org.eclipse.core.commands_3.4.0.I20080509-2000.jar jcommon-1.0.12.jar jfreechart-1.0.9.jar jfreechart-1.0.9-swt.jar osgi.jar diff --git a/ddms/app/src/com/android/ddms/PrefsDialog.java b/ddms/app/src/com/android/ddms/PrefsDialog.java index 418b8ba..c957a89 100644 --- a/ddms/app/src/com/android/ddms/PrefsDialog.java +++ b/ddms/app/src/com/android/ddms/PrefsDialog.java @@ -294,7 +294,11 @@ public final class PrefsDialog { dlg.setPreferenceStore(mPrefStore); // run it - dlg.open(); + try { + dlg.open(); + } catch (Throwable t) { + Log.e("ddms", t); + } // save prefs try { diff --git a/ddms/app/src/com/android/ddms/UIThread.java b/ddms/app/src/com/android/ddms/UIThread.java index 3304fff..cb6786a 100644 --- a/ddms/app/src/com/android/ddms/UIThread.java +++ b/ddms/app/src/com/android/ddms/UIThread.java @@ -50,6 +50,10 @@ import com.android.ddmuilib.logcat.LogColors; import com.android.ddmuilib.logcat.LogFilter; import com.android.ddmuilib.logcat.LogPanel; import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager; +import com.android.menubar.IMenuBarCallback; +import com.android.menubar.IMenuBarEnhancer; +import com.android.menubar.IMenuBarEnhancer.MenuBarMode; +import com.android.menubar.MenuBarEnhancer; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceStore; @@ -84,7 +88,6 @@ import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; -import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Sash; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TabFolder; @@ -101,6 +104,8 @@ import java.util.ArrayList; * when {@link IDevice} / {@link Client} selection changes. */ public class UIThread implements IUiSelectionListener, IClientChangeListener { + private static final String APP_NAME = "DDMS"; + /* * UI tab panel definitions. The constants here must match up with the array * indices in mPanels. PANEL_CLIENT_LIST is a "virtual" panel representing @@ -414,10 +419,10 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener { /** * Create SWT objects and drive the user interface event loop. - * @param location location of the folder that contains ddms. + * @param ddmsParentLocation location of the folder that contains ddms. */ public void runUI(String ddmsParentLocation) { - Display.setAppName("ddms"); + Display.setAppName(APP_NAME); mDisplay = new Display(); final Shell shell = new Shell(mDisplay); @@ -435,11 +440,11 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener { // dialog box only run in UI thread.. mDisplay.asyncExec(new Runnable() { public void run() { - Shell shell = mDisplay.getActiveShell(); + Shell activeShell = mDisplay.getActiveShell(); if (logLevel == LogLevel.ERROR) { - MessageDialog.openError(shell, tag, message); + MessageDialog.openError(activeShell, tag, message); } else { - MessageDialog.openWarning(shell, tag, message); + MessageDialog.openWarning(activeShell, tag, message); } } }); @@ -556,20 +561,20 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener { shell.addControlListener(new ControlListener() { public void controlMoved(ControlEvent e) { // get the new x/y - Rectangle rect = shell.getBounds(); + Rectangle controlBounds = shell.getBounds(); // store in pref file - PreferenceStore prefs = PrefsDialog.getStore(); - prefs.setValue(PrefsDialog.SHELL_X, rect.x); - prefs.setValue(PrefsDialog.SHELL_Y, rect.y); + PreferenceStore currentPrefs = PrefsDialog.getStore(); + currentPrefs.setValue(PrefsDialog.SHELL_X, controlBounds.x); + currentPrefs.setValue(PrefsDialog.SHELL_Y, controlBounds.y); } public void controlResized(ControlEvent e) { // get the new w/h - Rectangle rect = shell.getBounds(); + Rectangle controlBounds = shell.getBounds(); // store in pref file - PreferenceStore prefs = PrefsDialog.getStore(); - prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width); - prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height); + PreferenceStore currentPrefs = PrefsDialog.getStore(); + currentPrefs.setValue(PrefsDialog.SHELL_WIDTH, controlBounds.width); + currentPrefs.setValue(PrefsDialog.SHELL_HEIGHT, controlBounds.height); } }); } @@ -624,41 +629,31 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener { shell.addControlListener(new ControlListener() { public void controlMoved(ControlEvent e) { // get the new x/y - Rectangle rect = shell.getBounds(); + Rectangle controlBounds = shell.getBounds(); // store in pref file - PreferenceStore prefs = PrefsDialog.getStore(); - prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x); - prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y); + PreferenceStore currentPrefs = PrefsDialog.getStore(); + currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_X, controlBounds.x); + currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, controlBounds.y); } public void controlResized(ControlEvent e) { // get the new w/h - Rectangle rect = shell.getBounds(); + Rectangle controlBounds = shell.getBounds(); // store in pref file - PreferenceStore prefs = PrefsDialog.getStore(); - prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width); - prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height); + PreferenceStore currentPrefs = PrefsDialog.getStore(); + currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, controlBounds.width); + currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, controlBounds.height); } }); } /* - * Set the confirm-before-close dialog. TODO: enable/disable in prefs. TODO: - * is there any point in having this? + * Set the confirm-before-close dialog. */ private void setConfirmClose(final Shell shell) { - if (true) - return; - - shell.addListener(SWT.Close, new Listener() { - public void handleEvent(Event event) { - int style = SWT.APPLICATION_MODAL | SWT.YES | SWT.NO; - MessageBox msgBox = new MessageBox(shell, style); - msgBox.setText("Confirm..."); - msgBox.setMessage("Close DDM?"); - event.doit = (msgBox.open() == SWT.YES); - } - }); + // Note: there was some commented out code to display a confirmation box + // when closing. The feature seems unnecessary and the code was not being + // used, so it has been removed. } /* @@ -677,8 +672,6 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener { actionItem.setText("&Actions"); MenuItem deviceItem = new MenuItem(menuBar, SWT.CASCADE); deviceItem.setText("&Device"); - MenuItem helpItem = new MenuItem(menuBar, SWT.CASCADE); - helpItem.setText("&Help"); // create top-level menus Menu fileMenu = new Menu(menuBar); @@ -689,22 +682,11 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener { actionItem.setMenu(actionMenu); Menu deviceMenu = new Menu(menuBar); deviceItem.setMenu(deviceMenu); - Menu helpMenu = new Menu(menuBar); - helpItem.setMenu(helpMenu); MenuItem item; // create File menu items item = new MenuItem(fileMenu, SWT.NONE); - item.setText("&Preferences..."); - item.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - PrefsDialog.run(shell); - } - }); - - item = new MenuItem(fileMenu, SWT.NONE); item.setText("&Static Port Configuration..."); item.addSelectionListener(new SelectionAdapter() { @Override @@ -714,18 +696,36 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener { } }); - new MenuItem(fileMenu, SWT.SEPARATOR); + IMenuBarEnhancer enhancer = MenuBarEnhancer.setupMenu(APP_NAME, fileMenu, + new IMenuBarCallback() { + public void printError(String format, Object... args) { + Log.e("DDMS Menu Bar", String.format(format, args)); + } - item = new MenuItem(fileMenu, SWT.NONE); - item.setText("E&xit\tCtrl-Q"); - item.setAccelerator('Q' | (Main.isMac() ? SWT.COMMAND : SWT.CONTROL)); - item.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - shell.close(); + public void onPreferencesMenuSelected() { + PrefsDialog.run(shell); + } + + public void onAboutMenuSelected() { + AboutDialog dlg = new AboutDialog(shell); + dlg.open(); } }); + if (enhancer.getMenuBarMode() == MenuBarMode.GENERIC) { + new MenuItem(fileMenu, SWT.SEPARATOR); + + item = new MenuItem(fileMenu, SWT.NONE); + item.setText("E&xit\tCtrl-Q"); + item.setAccelerator('Q' | (Main.isMac() ? SWT.COMMAND : SWT.CONTROL)); + item.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + shell.close(); + } + }); + } + // create edit menu items mCopyMenuItem = new MenuItem(editMenu, SWT.NONE); mCopyMenuItem.setText("&Copy\tCtrl-C"); @@ -900,32 +900,6 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener { } }); - // create Help menu items - item = new MenuItem(helpMenu, SWT.NONE); - item.setText("&Contents..."); - item.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - int style = SWT.APPLICATION_MODAL | SWT.OK; - MessageBox msgBox = new MessageBox(shell, style); - msgBox.setText("Help!"); - msgBox.setMessage("Help wanted."); - msgBox.open(); - } - }); - - new MenuItem(helpMenu, SWT.SEPARATOR); - - item = new MenuItem(helpMenu, SWT.NONE); - item.setText("&About..."); - item.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - AboutDialog dlg = new AboutDialog(shell); - dlg.open(); - } - }); - // tell the shell to use this menu shell.setMenuBar(menuBar); } @@ -1007,7 +981,9 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener { e.y = Math.max(Math.min(e.y, bottom), 100); if (e.y != sashRect.y) { sashData.top = new FormAttachment(0, e.y); - prefs.setValue(PREFERENCE_LOGSASH, e.y); + if (prefs != null) { + prefs.setValue(PREFERENCE_LOGSASH, e.y); + } panelArea.layout(); } } @@ -1199,7 +1175,9 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener { e.x = Math.max(Math.min(e.x, right), minPanelWidth); if (e.x != sashRect.x) { sashData.left = new FormAttachment(0, e.x); - prefs.setValue(PREFERENCE_SASH, e.x); + if (prefs != null) { + prefs.setValue(PREFERENCE_SASH, e.x); + } comp.layout(); } } @@ -1282,13 +1260,13 @@ public class UIThread implements IUiSelectionListener, IClientChangeListener { @Override public void widgetSelected(SelectionEvent e) { // disable the other actions and record current index - for (int i = 0 ; i < mLogLevelActions.length; i++) { - ToolItemAction a = mLogLevelActions[i]; + for (int k = 0 ; k < mLogLevelActions.length; k++) { + ToolItemAction a = mLogLevelActions[k]; if (a == newAction) { a.setChecked(true); // set the log level - mLogPanel.setCurrentFilterLogLevel(i+2); + mLogPanel.setCurrentFilterLogLevel(k+2); } else { a.setChecked(false); } diff --git a/ddms/libs/ddmuilib/src/com/android/ddmuilib/ImageLoader.java b/ddms/libs/ddmuilib/src/com/android/ddmuilib/ImageLoader.java index 40cbd1d..fd480f6 100644 --- a/ddms/libs/ddmuilib/src/com/android/ddmuilib/ImageLoader.java +++ b/ddms/libs/ddmuilib/src/com/android/ddmuilib/ImageLoader.java @@ -140,13 +140,11 @@ public class ImageLoader { if (imageStream != null) { img = new Image(display, imageStream); - if (img == null) { - throw new NullPointerException("couldn't load " + tmp); - } - mLoadedImages.put(filename, img); + } - return img; + if (img == null) { + throw new RuntimeException("Failed to load " + tmp); } } @@ -159,7 +157,6 @@ public class ImageLoader { * Extra parameters allows for creation of a replacement image of the * loading failed. * - * @param loader the image loader used. * @param display the Display object * @param fileName the file name * @param width optional width to create replacement Image. If -1, null be diff --git a/draw9patch/src/com/android/draw9patch/ui/MainFrame.java b/draw9patch/src/com/android/draw9patch/ui/MainFrame.java index d5b6409..57f6cd9 100644 --- a/draw9patch/src/com/android/draw9patch/ui/MainFrame.java +++ b/draw9patch/src/com/android/draw9patch/ui/MainFrame.java @@ -40,9 +40,15 @@ public class MainFrame extends JFrame { private JMenuItem saveMenuItem; private ImageEditorPanel imageEditor; + private static final String TITLE_FORMAT = "Draw 9-patch: %s"; + public MainFrame(String path) throws HeadlessException { super("Draw 9-patch"); + if (path != null) { + setTitle(String.format(TITLE_FORMAT, path)); + } + buildActions(); buildMenuBar(); buildContent(); @@ -164,6 +170,7 @@ public class MainFrame extends JFrame { protected void done() { try { showImageEditor(get(), file.getAbsolutePath()); + setTitle(String.format(TITLE_FORMAT, file.getAbsolutePath())); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/icons/requestFocus.png b/eclipse/plugins/com.android.ide.eclipse.adt/icons/requestFocus.png Binary files differnew file mode 100644 index 0000000..205a032 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/icons/requestFocus.png diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/EditTextRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/EditTextRule.java new file mode 100644 index 0000000..dfe9d6f --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/EditTextRule.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2010 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.common.layout; + +import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.REQUEST_FOCUS; + +import com.android.ide.common.api.IMenuCallback; +import com.android.ide.common.api.INode; +import com.android.ide.common.api.INodeHandler; +import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.InsertType; +import com.android.ide.common.api.MenuAction; + +import java.util.List; + +/** + * An {@link IViewRule} for android.widget.EditText. + */ +public class EditTextRule extends BaseViewRule { + + @Override + public void onCreate(INode node, INode parent, InsertType insertType) { + super.onCreate(node, parent, insertType); + + if (parent != null) { + INode focus = findFocus(findRoot(parent)); + if (focus == null) { + // Add <requestFocus> + node.appendChild(REQUEST_FOCUS); + } + } + } + + /** + * {@inheritDoc} + * <p> + * Adds a "Request Focus" menu item. + */ + @Override + public List<MenuAction> getContextMenu(final INode selectedNode) { + final boolean hasFocus = hasFocus(selectedNode); + final String label = hasFocus ? "Clear Focus" : "Request Focus"; + + IMenuCallback onChange = new IMenuCallback() { + public void action(MenuAction menuAction, String valueId, Boolean newValue) { + selectedNode.editXml(label, new INodeHandler() { + public void handle(INode node) { + INode focus = findFocus(findRoot(node)); + if (focus != null && focus.getParent() != null) { + focus.getParent().removeChild(focus); + } + if (!hasFocus) { + node.appendChild(REQUEST_FOCUS); + } + } + }); + } + }; + + return concatenate(super.getContextMenu(selectedNode), + new MenuAction.Action("_setfocus", label, null, onChange)); //$NON-NLS-1$ + } + + /** Returns true if the given node currently has focus */ + private static boolean hasFocus(INode node) { + INode focus = findFocus(node); + if (focus != null) { + return focus.getParent() == node; + } + + return false; + } + + /** Returns the root/top level node in the view hierarchy that contains the given node */ + private static INode findRoot(INode node) { + // First find the parent + INode root = node; + while (root != null) { + INode parent = root.getParent(); + if (parent == null) { + break; + } else { + root = parent; + } + } + + return root; + } + + /** Finds the focus node (not the node containing focus, but the actual request focus node + * under a given node */ + private static INode findFocus(INode node) { + if (node.getFqcn().equals(REQUEST_FOCUS)) { + return node; + } + + for (INode child : node.getChildren()) { + INode focus = findFocus(child); + if (focus != null) { + return focus; + } + } + return null; + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java index f88e7ab..bb04498 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java @@ -29,10 +29,10 @@ import com.android.sdklib.SdkConstants; * </ul> */ public class LayoutConstants { - /** The element name in a <code><view class="..."></code> element. */ + /** The element name in a {@code <view class="...">} element. */ public static final String VIEW = "view"; //$NON-NLS-1$ - /** The attribute name in a <code><view class="..."></code> element. */ + /** The attribute name in a {@code <view class="...">} element. */ public static final String ATTR_CLASS = "class"; //$NON-NLS-1$ public static final String ATTR_ON_CLICK = "onClick"; //$NON-NLS-1$ diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java index 0384ad5..c7bc5a1 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/descriptors/DescriptorsUtils.java @@ -32,6 +32,7 @@ import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX; import static com.android.ide.common.layout.LayoutConstants.RELATIVE_LAYOUT; import static com.android.ide.common.layout.LayoutConstants.VALUE_FILL_PARENT; import static com.android.ide.common.layout.LayoutConstants.VALUE_WRAP_CONTENT; +import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.REQUEST_FOCUS; import com.android.ide.common.api.IAttributeInfo.Format; import com.android.ide.common.resources.platform.AttributeInfo; @@ -699,6 +700,12 @@ public final class DescriptorsUtils { // if this ui_node is a layout and we're adding it to a document, use match_parent for // both W/H. Otherwise default to wrap_layout. ElementDescriptor descriptor = node.getDescriptor(); + + if (descriptor.getXmlLocalName().equals(REQUEST_FOCUS)) { + // Don't add ids etc to <requestFocus> + return; + } + boolean fill = descriptor.hasChildren() && node.getUiParent() instanceof UiDocumentNode; node.setAttributeValue( diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java index b61c069..4613492 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/descriptors/LayoutDescriptors.java @@ -42,20 +42,27 @@ import java.util.Map.Entry; public final class LayoutDescriptors implements IDescriptorProvider { /** - * The XML name of the special <include> layout tag. + * The XML name of the special {@code <include>} layout tag. * A synthetic element with that name is created as part of the view descriptors list * returned by {@link #getViewDescriptors()}. */ public static final String VIEW_INCLUDE = "include"; //$NON-NLS-1$ /** - * The XML name of the special <merge> layout tag. + * The XML name of the special {@code <merge>} layout tag. * A synthetic element with that name is created as part of the view descriptors list * returned by {@link #getViewDescriptors()}. */ public static final String VIEW_MERGE = "merge"; //$NON-NLS-1$ /** + * The XML name of the special {@code <requestFocus>} layout tag. + * A synthetic element with that name is created as part of the view descriptors list + * returned by {@link #getViewDescriptors()}. + */ + public static final String REQUEST_FOCUS = "requestFocus";//$NON-NLS-1$ + + /** * The attribute name of the include tag's url naming the resource to be inserted * <p> * <b>NOTE</b>: The layout attribute is NOT in the Android namespace! @@ -174,6 +181,10 @@ public final class LayoutDescriptors implements IDescriptorProvider { fixSuperClasses(infoDescMap); + ViewElementDescriptor requestFocus = createRequestFocus(); + newViews.add(requestFocus); + newDescriptors.add(requestFocus); + // The <merge> tag can only be a root tag, so it is added at the end. // It gets everything else as children but it is not made a child itself. ViewElementDescriptor mergeTag = createMerge(newLayouts); @@ -342,7 +353,7 @@ public final class LayoutDescriptors implements IDescriptorProvider { } /** - * Creates and return a new <merge> descriptor. + * Creates and return a new {@code <merge>} descriptor. * @param knownLayouts A list of all known layout view descriptors, used to find the * FrameLayout descriptor and extract its layout attributes. */ @@ -368,6 +379,27 @@ public final class LayoutDescriptors implements IDescriptorProvider { } /** + * Creates and return a new {@code <requestFocus>} descriptor. + * @param knownLayouts A list of all known layout view descriptors, used to find the + * FrameLayout descriptor and extract its layout attributes. + */ + private ViewElementDescriptor createRequestFocus() { + String xml_name = REQUEST_FOCUS; + + // Create the include descriptor + return new ViewElementDescriptor( + xml_name, // xml_name + xml_name, // ui_name + xml_name, // "class name"; the GLE only treats this as an element tag + "Requests focus for the parent element or one of its descendants", // tooltip + null, // sdk_url + null, // attributes + null, // layout attributes + null, // children + false /* mandatory */); + } + + /** * Finds the descriptor and retrieves all its layout attributes. */ private AttributeDescriptor[] findViewLayoutAttributes( diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java index f7f23da..2227531 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteControl.java @@ -1125,9 +1125,11 @@ public class PaletteControl extends Composite { final static int TOGGLE_ALPHABETICAL = 2; final static int TOGGLE_AUTO_CLOSE = 3; final static int REFRESH = 4; + final static int RESET = 5; ToggleViewOptionAction(String title, int action, boolean checked) { - super(title, action == REFRESH ? IAction.AS_PUSH_BUTTON : IAction.AS_CHECK_BOX); + super(title, (action == REFRESH || action == RESET) ? IAction.AS_PUSH_BUTTON + : IAction.AS_CHECK_BOX); mAction = action; if (checked) { setChecked(checked); @@ -1153,6 +1155,13 @@ public class PaletteControl extends Composite { mPreviewIconFactory.refresh(); refreshPalette(); break; + case RESET: + mAlphabetical = false; + mCategories = true; + mAutoClose = true; + mPaletteMode = PaletteMode.SMALL_PREVIEW; + refreshPalette(); + break; } savePaletteMode(); } @@ -1197,6 +1206,11 @@ public class PaletteControl extends Composite { manager.add(new ToggleViewOptionAction("Auto Close Previous", ToggleViewOptionAction.TOGGLE_AUTO_CLOSE, mAutoClose)); + manager.add(new Separator()); + manager.add(new ToggleViewOptionAction("Reset", + ToggleViewOptionAction.RESET, + false)); + Menu menu = manager.createContextMenu(PaletteControl.this); menu.setLocation(x, y); menu.setVisible(true); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/extra-view-metadata.xml b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/extra-view-metadata.xml index 0870f69..442b76c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/extra-view-metadata.xml +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/extra-view-metadata.xml @@ -298,6 +298,9 @@ <category name="Advanced"> <view + class="requestFocus" + render="skip" /> + <view class="android.view.View" render="skip" /> <view diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java index 930e8de..4db603e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/AndroidLaunchController.java @@ -44,9 +44,8 @@ import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.sdklib.AndroidVersion; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.NullSdkLog; -import com.android.sdklib.SdkConstants; +import com.android.sdklib.internal.avd.AvdInfo; import com.android.sdklib.internal.avd.AvdManager; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdklib.xml.ManifestData; import org.eclipse.core.resources.IFile; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DeviceChooserDialog.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DeviceChooserDialog.java index a9c8af8..b0dfea3 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DeviceChooserDialog.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/DeviceChooserDialog.java @@ -29,7 +29,7 @@ import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.ide.eclipse.ddms.DdmsPlugin; import com.android.sdklib.AndroidVersion; import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo; +import com.android.sdklib.internal.avd.AvdInfo; import com.android.sdkuilib.internal.widgets.AvdSelector; import com.android.sdkuilib.internal.widgets.AvdSelector.DisplayMode; import com.android.sdkuilib.internal.widgets.AvdSelector.IAvdFilter; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/EmulatorConfigTab.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/EmulatorConfigTab.java index 3705cc2..1fc72fb 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/EmulatorConfigTab.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/EmulatorConfigTab.java @@ -26,8 +26,8 @@ import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.NullSdkLog; +import com.android.sdklib.internal.avd.AvdInfo; import com.android.sdklib.internal.avd.AvdManager; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdkuilib.internal.widgets.AvdSelector; import com.android.sdkuilib.internal.widgets.AvdSelector.DisplayMode; diff --git a/eclipse/scripts/create_ddms_symlinks.sh b/eclipse/scripts/create_ddms_symlinks.sh index 8791316..48f5c51 100755 --- a/eclipse/scripts/create_ddms_symlinks.sh +++ b/eclipse/scripts/create_ddms_symlinks.sh @@ -63,11 +63,12 @@ for i in prebuilt/common/jfreechart/*.jar; do cpfile $DEST $i done -LIBS="ddmlib ddmuilib" +COPY_LIBS="ddmlib ddmuilib" +ALL_LIBS="$COPY_LIBS swtmenubar" echo "make java libs ..." -make -j3 showcommands $LIBS || die "DDMS: Fail to build one of $LIBS." +make -j3 showcommands $ALL_LIBS || die "DDMS: Fail to build one of $ALL_LIBS." -for LIB in $LIBS; do +for LIB in $COPY_LIBS; do cpfile $DEST out/host/$PLATFORM/framework/$LIB.jar done diff --git a/eclipse/scripts/create_hierarchyviewer_symlinks.sh b/eclipse/scripts/create_hierarchyviewer_symlinks.sh index e0439ef..47dbe7f 100755 --- a/eclipse/scripts/create_hierarchyviewer_symlinks.sh +++ b/eclipse/scripts/create_hierarchyviewer_symlinks.sh @@ -60,10 +60,11 @@ DEST=$BASE/libs mkdir -p $DEST -LIBS="hierarchyviewerlib " +COPY_LIBS="hierarchyviewerlib" +ALL_LIBS="$COPY_LIBS swtmenubar" echo "make java libs ..." -make -j3 showcommands $LIBS || die "Hierarchy Viewer: Fail to build one of $LIBS." +make -j3 showcommands $ALL_LIBS || die "Hierarchy Viewer: Fail to build one of $ALL_LIBS." -for LIB in $LIBS; do +for LIB in $COPY_LIBS; do cpfile $DEST out/host/$PLATFORM/framework/$LIB.jar done diff --git a/hierarchyviewer2/app/.classpath b/hierarchyviewer2/app/.classpath index d75889a..c5a657c 100644 --- a/hierarchyviewer2/app/.classpath +++ b/hierarchyviewer2/app/.classpath @@ -7,5 +7,6 @@ <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/> <classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/> <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/> + <classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/swtmenubar.jar" sourcepath="/ANDROID_SRC/sdk/swtmenubar/src"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/hierarchyviewer2/app/Android.mk b/hierarchyviewer2/app/Android.mk index d940403..0e00273 100644 --- a/hierarchyviewer2/app/Android.mk +++ b/hierarchyviewer2/app/Android.mk @@ -27,7 +27,8 @@ LOCAL_JAVA_LIBRARIES := \ swt \ org.eclipse.jface_3.4.2.M20090107-0800 \ org.eclipse.core.commands_3.4.0.I20080509-2000 \ - sdklib + sdklib \ + swtmenubar LOCAL_MODULE := hierarchyviewer2 diff --git a/hierarchyviewer2/app/README b/hierarchyviewer2/app/README new file mode 100755 index 0000000..c00ef99 --- /dev/null +++ b/hierarchyviewer2/app/README @@ -0,0 +1,69 @@ +Using the Eclipse project HierarchyViewer +----------------------------------------- + +HierarchyViewer requires some external libraries to compile. +If you build HierarchyViewer using the makefile, you have nothing +to configure. However if you want to develop on HierarchyViewer +using Eclipse, you need to perform the following configuration. + + +------- +1- Projects required in Eclipse +------- + +To run HierarchyViewer from Eclipse, you need to import the following 5 projects: + + - sdk/hierarchyviewer2/app + - sdk/hierarchyviewer2/libs/hierarchyviewerlib/ + - sdk/ddms/libs/ddmlib + - sdk/ddms/libs/ddmuilib + - sdk/sdkmanager/libs/sdklib + + +------- +2- HierarchyViewer requires some SWT JARs to compile. +------- + +SWT is available in the tree under prebuild/<platform>/swt + +Because the build path cannot contain relative path that are not inside +the project directory, the .classpath file references a user library +called ANDROID_SWT. + +In order to compile the project: +- Open Preferences > Java > Build Path > User Libraries + +- Create a new user library named ANDROID_SWT +- Add the following 4 JAR files: + + - prebuilt/<platform>/swt/swt.jar + - prebuilt/common/eclipse/org.eclipse.core.commands_3.*.jar + - prebuilt/common/eclipse/org.eclipse.equinox.common_3.*.jar + - prebuilt/common/eclipse/org.eclipse.jface_3.*.jar + + +------- +3- HierarchyViewer also requires the compiled SwtMenuBar library. +------- + +Build the swtmenubar library: +$ cd $TOP (top of Android tree) +$ . build/envsetup.sh && lunch sdk-eng +$ sdk/eclipse/scripts/create_sdkman_symlinks.sh + +Define a classpath variable in Eclipse: +- Open Preferences > Java > Build Path > Classpath Variables +- Create a new classpath variable named ANDROID_OUT_FRAMEWORK +- Set its folder value to <Android tree>/out/host/<platform>/framework +- Create a new classpath variable named ANDROID_SRC +- Set its folder value to <Android tree> + +You might need to clean the ddms project (Project > Clean...) after +you add the new classpath variable, otherwise previous errors might not +go away automatically. + +The ANDROID_SRC part should be optional. It allows you to have access to +the SwtMenuBar generic parts from the Java editor. + +-- +EOF diff --git a/hierarchyviewer2/app/src/com/android/hierarchyviewer/AboutDialog.java b/hierarchyviewer2/app/src/com/android/hierarchyviewer/AboutDialog.java index 3f973e7..150c70a 100644 --- a/hierarchyviewer2/app/src/com/android/hierarchyviewer/AboutDialog.java +++ b/hierarchyviewer2/app/src/com/android/hierarchyviewer/AboutDialog.java @@ -41,8 +41,8 @@ public class AboutDialog extends Dialog { public AboutDialog(Shell shell) { super(shell); ImageLoader imageLoader = ImageLoader.getLoader(HierarchyViewerDirector.class); - mSmallImage = imageLoader.loadImage("load-view-hierarchy.png", Display.getDefault()); //$NON-NLS-1$ - mAboutImage = imageLoader.loadImage("about.jpg", Display.getDefault()); //$NON-NLS-1$ + mSmallImage = imageLoader.loadImage("sdk-hierarchyviewer-16.png", Display.getDefault()); //$NON-NLS-1$ + mAboutImage = imageLoader.loadImage("sdk-hierarchyviewer-128.png", Display.getDefault()); //$NON-NLS-1$ } @Override diff --git a/hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java b/hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java index 3a0a0e9..54a5fd6 100644 --- a/hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java +++ b/hierarchyviewer2/app/src/com/android/hierarchyviewer/HierarchyViewerApplication.java @@ -53,11 +53,15 @@ import com.android.hierarchyviewerlib.ui.PropertyViewer; import com.android.hierarchyviewerlib.ui.TreeView; import com.android.hierarchyviewerlib.ui.TreeViewControls; import com.android.hierarchyviewerlib.ui.TreeViewOverview; +import com.android.menubar.IMenuBarEnhancer; +import com.android.menubar.MenuBarEnhancer; +import com.android.menubar.IMenuBarEnhancer.MenuBarMode; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.window.ApplicationWindow; import org.eclipse.swt.SWT; +import org.eclipse.swt.SWTException; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.custom.StackLayout; import org.eclipse.swt.events.SelectionEvent; @@ -81,6 +85,7 @@ import org.eclipse.swt.widgets.Shell; public class HierarchyViewerApplication extends ApplicationWindow { + private static final String APP_NAME = "Hierarchy Viewer"; private static final int INITIAL_WIDTH = 1024; private static final int INITIAL_HEIGHT = 768; @@ -148,7 +153,7 @@ public class HierarchyViewerApplication extends ApplicationWindow { @Override protected void configureShell(Shell shell) { super.configureShell(shell); - shell.setText("Hierarchy Viewer"); + shell.setText(APP_NAME); ImageLoader imageLoader = ImageLoader.getLoader(HierarchyViewerDirector.class); Image image = imageLoader.loadImage("sdk-hierarchyviewer-128.png", Display.getDefault()); //$NON-NLS-1$ shell.setImage(image); @@ -162,7 +167,14 @@ public class HierarchyViewerApplication extends ApplicationWindow { public void run() { setBlockOnOpen(true); - open(); + try { + open(); + } catch (SWTException e) { + // Ignore "widget disposed" errors after we closed. + if (!getShell().isDisposed()) { + throw e; + } + } TreeViewModel.getModel().removeTreeChangeListener(mTreeChangeListener); PixelPerfectModel.getModel().removeImageChangeListener(mImageChangeListener); @@ -581,12 +593,16 @@ public class HierarchyViewerApplication extends ApplicationWindow { MenuManager mm = getMenuBarManager(); mm.removeAll(); - String os = System.getProperty("os.name"); //$NON-NLS-1$ - if (os.startsWith("Mac OS") == false) { //$NON-NLS-1$ - MenuManager file = new MenuManager("&File"); + MenuManager file = new MenuManager("&File"); + IMenuBarEnhancer enhancer = MenuBarEnhancer.setupMenuManager( + APP_NAME, + getShell().getDisplay(), + file, + AboutAction.getAction(getShell()), + null /*preferencesAction*/, + QuitAction.getAction()); + if (enhancer.getMenuBarMode() == MenuBarMode.GENERIC) { mm.add(file); - - file.add(QuitAction.getAction()); } MenuManager device = new MenuManager("&Devices"); @@ -596,11 +612,6 @@ public class HierarchyViewerApplication extends ApplicationWindow { device.add(LoadViewHierarchyAction.getAction()); device.add(InspectScreenshotAction.getAction()); - MenuManager help = new MenuManager("&Help"); - mm.add(help); - - help.add(AboutAction.getAction(getShell())); - mm.updateAll(true); mDeviceViewButton.setSelection(true); @@ -626,12 +637,16 @@ public class HierarchyViewerApplication extends ApplicationWindow { MenuManager mm = getMenuBarManager(); mm.removeAll(); - String os = System.getProperty("os.name"); //$NON-NLS-1$ - if (os.startsWith("Mac OS") == false) { //$NON-NLS-1$ - MenuManager file = new MenuManager("&File"); + MenuManager file = new MenuManager("&File"); + IMenuBarEnhancer enhancer = MenuBarEnhancer.setupMenuManager( + APP_NAME, + getShell().getDisplay(), + file, + AboutAction.getAction(getShell()), + null /*preferencesAction*/, + QuitAction.getAction()); + if (enhancer.getMenuBarMode() == MenuBarMode.GENERIC) { mm.add(file); - - file.add(QuitAction.getAction()); } MenuManager treeViewMenu = new MenuManager("&Tree View"); @@ -646,11 +661,6 @@ public class HierarchyViewerApplication extends ApplicationWindow { treeViewMenu.add(InvalidateAction.getAction()); treeViewMenu.add(RequestLayoutAction.getAction()); - MenuManager help = new MenuManager("&Help"); - mm.add(help); - - help.add(AboutAction.getAction(getShell())); - mm.updateAll(true); mDeviceViewButton.setSelection(false); @@ -676,12 +686,16 @@ public class HierarchyViewerApplication extends ApplicationWindow { MenuManager mm = getMenuBarManager(); mm.removeAll(); - String os = System.getProperty("os.name"); //$NON-NLS-1$ - if (os.startsWith("Mac OS") == false) { //$NON-NLS-1$ - MenuManager file = new MenuManager("&File"); + MenuManager file = new MenuManager("&File"); + IMenuBarEnhancer enhancer = MenuBarEnhancer.setupMenuManager( + APP_NAME, + getShell().getDisplay(), + file, + AboutAction.getAction(getShell()), + null /*preferencesAction*/, + QuitAction.getAction()); + if (enhancer.getMenuBarMode() == MenuBarMode.GENERIC) { mm.add(file); - - file.add(QuitAction.getAction()); } MenuManager pixelPerfect = new MenuManager("&Pixel Perfect"); @@ -695,11 +709,6 @@ public class HierarchyViewerApplication extends ApplicationWindow { mm.add(pixelPerfect); - MenuManager help = new MenuManager("&Help"); - mm.add(help); - - help.add(AboutAction.getAction(getShell())); - mm.updateAll(true); mDeviceViewButton.setSelection(false); diff --git a/hierarchyviewer2/app/src/com/android/hierarchyviewer/actions/AboutAction.java b/hierarchyviewer2/app/src/com/android/hierarchyviewer/actions/AboutAction.java index 0c7c7b2..332b2dc 100644 --- a/hierarchyviewer2/app/src/com/android/hierarchyviewer/actions/AboutAction.java +++ b/hierarchyviewer2/app/src/com/android/hierarchyviewer/actions/AboutAction.java @@ -41,7 +41,7 @@ public class AboutAction extends Action implements ImageAction { this.mShell = shell; setAccelerator(SWT.MOD1 + 'A'); ImageLoader imageLoader = ImageLoader.getLoader(HierarchyViewerDirector.class); - mImage = imageLoader.loadImage("about-small.jpg", Display.getDefault()); //$NON-NLS-1$ + mImage = imageLoader.loadImage("sdk-hierarchyviewer-16.png", Display.getDefault()); //$NON-NLS-1$ setImageDescriptor(ImageDescriptor.createFromImage(mImage)); setToolTipText("Shows the about dialog"); } diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk b/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk index ded20e1..3ca63dd 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk @@ -16,7 +16,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_JAVA_RESOURCE_DIRS := resources +LOCAL_JAVA_RESOURCE_DIRS := ../src LOCAL_JAR_MANIFEST := ../manifest.txt diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/auto-refresh.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/auto-refresh.png Binary files differindex 240862f..240862f 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/auto-refresh.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/auto-refresh.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/capture-psd.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/capture-psd.png Binary files differindex 0f25426..0f25426 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/capture-psd.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/capture-psd.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/device-view-selected.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/device-view-selected.png Binary files differindex fd107ed..fd107ed 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/device-view-selected.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/device-view-selected.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/device-view.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/device-view.png Binary files differindex 9a7eed4..9a7eed4 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/device-view.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/device-view.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/display.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/display.png Binary files differindex a9de0ec..a9de0ec 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/display.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/display.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/filtered.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/filtered.png Binary files differindex 4fcab3f..4fcab3f 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/filtered.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/filtered.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/green.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/green.png Binary files differindex 800000d..800000d 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/green.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/green.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/inspect-screenshot.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/inspect-screenshot.png Binary files differindex 6e51701..6e51701 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/inspect-screenshot.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/inspect-screenshot.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/invalidate.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/invalidate.png Binary files differindex ee75f69..ee75f69 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/invalidate.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/invalidate.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/load-all-views.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/load-all-views.png Binary files differindex 3329ec9..3329ec9 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/load-all-views.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/load-all-views.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/load-overlay.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/load-overlay.png Binary files differindex 4817252..4817252 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/load-overlay.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/load-overlay.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/load-view-hierarchy.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/load-view-hierarchy.png Binary files differindex 8f01dda..8f01dda 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/load-view-hierarchy.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/load-view-hierarchy.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/not-selected.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/not-selected.png Binary files differindex db6f13b..db6f13b 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/not-selected.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/not-selected.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/on-black.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/on-black.png Binary files differindex cd88803..cd88803 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/on-black.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/on-black.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/on-white.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/on-white.png Binary files differindex 5f05662..5f05662 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/on-white.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/on-white.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/pixel-perfect-view-selected.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/pixel-perfect-view-selected.png Binary files differindex 1e44000..1e44000 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/pixel-perfect-view-selected.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/pixel-perfect-view-selected.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/pixel-perfect-view.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/pixel-perfect-view.png Binary files differindex ec51cec..ec51cec 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/pixel-perfect-view.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/pixel-perfect-view.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/red.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/red.png Binary files differindex a2ab855..a2ab855 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/red.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/red.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/refresh-windows.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/refresh-windows.png Binary files differindex 8fddcae..8fddcae 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/refresh-windows.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/refresh-windows.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/request-layout.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/request-layout.png Binary files differindex 92a78c8..92a78c8 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/request-layout.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/request-layout.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/save.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/save.png Binary files differindex 2c0bab1..2c0bab1 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/save.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/save.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/sdk-hierarchyviewer-128.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/sdk-hierarchyviewer-128.png Binary files differindex 4535f22..4535f22 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/sdk-hierarchyviewer-128.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/sdk-hierarchyviewer-128.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/images/sdk-hierarchyviewer-16.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/sdk-hierarchyviewer-16.png Binary files differnew file mode 100755 index 0000000..8c3c23d --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/sdk-hierarchyviewer-16.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected-filtered-small.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected-filtered-small.png Binary files differindex 9ef6b34..9ef6b34 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected-filtered-small.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected-filtered-small.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected-filtered.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected-filtered.png Binary files differindex 1f59685..1f59685 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected-filtered.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected-filtered.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected-small.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected-small.png Binary files differindex 538e385..538e385 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected-small.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected-small.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected.png Binary files differindex 5cd5c3f..5cd5c3f 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/selected.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/selected.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/show-extras.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/show-extras.png Binary files differindex ba9c305..ba9c305 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/show-extras.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/show-extras.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/show-overlay.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/show-overlay.png Binary files differindex e39e90a..e39e90a 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/show-overlay.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/show-overlay.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/tree-view-selected.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/tree-view-selected.png Binary files differindex 175ad1f..175ad1f 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/tree-view-selected.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/tree-view-selected.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/tree-view.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/tree-view.png Binary files differindex 23aa424..23aa424 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/tree-view.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/tree-view.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/yellow.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/yellow.png Binary files differindex e9b5781..e9b5781 100644 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/yellow.png +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/images/yellow.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/about-small.jpg b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/about-small.jpg Binary files differdeleted file mode 100644 index 6fe9291..0000000 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/about-small.jpg +++ /dev/null diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/about.jpg b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/about.jpg Binary files differdeleted file mode 100644 index 8e10514..0000000 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/about.jpg +++ /dev/null diff --git a/sdkmanager/app/etc/android b/sdkmanager/app/etc/android index 2e17009..57ce733 100755 --- a/sdkmanager/app/etc/android +++ b/sdkmanager/app/etc/android @@ -105,9 +105,5 @@ if [ ! -d "$swtpath" ]; then exit 1 fi -if [ -z "$1" ]; then - echo "Starting Android SDK and AVD Manager" -fi - # need to use "java.ext.dirs" because "-jar" causes classpath to be ignored exec "$java_cmd" -Xmx256M $os_opts $java_debug -Dcom.android.sdkmanager.toolsdir="$progdir" -classpath "$jarpath:$swtpath/swt.jar" com.android.sdkmanager.Main "$@" diff --git a/sdkmanager/app/etc/android.bat b/sdkmanager/app/etc/android.bat index daa6b8a..d4b5939 100755 --- a/sdkmanager/app/etc/android.bat +++ b/sdkmanager/app/etc/android.bat @@ -45,7 +45,7 @@ for /f %%a in ('%java_exe% -jar lib\archquery.jar') do set swt_path=lib\%%a if "%1 %2"=="update sdk" goto StartUi
if not "%1"=="" goto EndTempCopy
:StartUi
- echo [INFO] Starting Android SDK and AVD Manager
+ rem Starting Android SDK and AVD Manager UI
rem We're now going to create a temp dir to hold all the Jar files needed
rem to run the android tool, copy them in the temp dir and finally execute
diff --git a/sdkmanager/app/src/com/android/sdkmanager/Main.java b/sdkmanager/app/src/com/android/sdkmanager/Main.java index 3936286..5ac5f4c 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/Main.java +++ b/sdkmanager/app/src/com/android/sdkmanager/Main.java @@ -26,9 +26,9 @@ import com.android.sdklib.ISdkLog; import com.android.sdklib.SdkConstants; import com.android.sdklib.SdkManager; import com.android.sdklib.IAndroidTarget.IOptionalLibrary; +import com.android.sdklib.internal.avd.AvdInfo; import com.android.sdklib.internal.avd.AvdManager; import com.android.sdklib.internal.avd.HardwareProperties; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdklib.internal.avd.HardwareProperties.HardwareProperty; import com.android.sdklib.internal.project.ProjectCreator; import com.android.sdklib.internal.project.ProjectProperties; @@ -306,10 +306,6 @@ public class Main { */ private void showMainWindow(boolean autoUpdate) { try { - // display a message talking about the command line version - System.out.printf("No command line parameters provided, launching UI.\n" + - "See 'android --help' for operations from the command line.\n"); - MessageBoxLog errorLogger = new MessageBoxLog( "SDK Manager", Display.getCurrent(), @@ -784,6 +780,18 @@ public class Main { * Displays the list of available Targets (Platforms and Add-ons) */ private void displayTargetList() { + + // Compact output, suitable for scripts. + if (mSdkCommandLine != null && mSdkCommandLine.getFlagCompact()) { + char eol = mSdkCommandLine.getFlagEolNull() ? '\0' : '\n'; + + for (IAndroidTarget target : mSdkManager.getTargets()) { + mSdkLog.printf("%1$s%2$c", target.hashString(), eol); + } + + return; + } + mSdkLog.printf("Available Android targets:\n"); int index = 1; @@ -862,16 +870,30 @@ public class Main { * @param avdManager */ public void displayAvdList(AvdManager avdManager) { - mSdkLog.printf("Available Android Virtual Devices:\n"); AvdInfo[] avds = avdManager.getValidAvds(); + + // Compact output, suitable for scripts. + if (mSdkCommandLine != null && mSdkCommandLine.getFlagCompact()) { + char eol = mSdkCommandLine.getFlagEolNull() ? '\0' : '\n'; + + for (int index = 0 ; index < avds.length ; index++) { + AvdInfo info = avds[index]; + mSdkLog.printf("%1$s%2$c", info.getName(), eol); + } + + return; + } + + mSdkLog.printf("Available Android Virtual Devices:\n"); + for (int index = 0 ; index < avds.length ; index++) { AvdInfo info = avds[index]; if (index > 0) { mSdkLog.printf("---------\n"); } mSdkLog.printf(" Name: %s\n", info.getName()); - mSdkLog.printf(" Path: %s\n", info.getPath()); + mSdkLog.printf(" Path: %s\n", info.getDataFolderPath()); // get the target of the AVD IAndroidTarget target = info.getTarget(); @@ -920,7 +942,8 @@ public class Main { mSdkLog.printf("---------\n"); } mSdkLog.printf(" Name: %s\n", info.getName() == null ? "--" : info.getName()); - mSdkLog.printf(" Path: %s\n", info.getPath() == null ? "--" : info.getPath()); + mSdkLog.printf(" Path: %s\n", + info.getDataFolderPath() == null ? "--" : info.getDataFolderPath()); String error = info.getErrorMessage(); mSdkLog.printf(" Error: %s\n", error == null ? "Uknown error" : error); @@ -987,7 +1010,7 @@ public class Main { if (paramFolderPath != null) { avdFolder = new File(paramFolderPath); } else { - avdFolder = AvdManager.AvdInfo.getAvdFolder(avdName); + avdFolder = AvdInfo.getDefaultAvdFolder(avdManager, avdName); } // Validate skin is either default (empty) or NNNxMMM or a valid skin name. @@ -1113,7 +1136,7 @@ public class Main { // check if paths are the same. Use File methods to account for OS idiosyncrasies. try { File f1 = new File(paramFolderPath).getCanonicalFile(); - File f2 = new File(info.getPath()).getCanonicalFile(); + File f2 = new File(info.getDataFolderPath()).getCanonicalFile(); if (f1.equals(f2)) { // same canonical path, so not actually a move paramFolderPath = null; @@ -1139,7 +1162,7 @@ public class Main { File originalFolder = new File( AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD, info.getName() + AvdManager.AVD_FOLDER_EXTENSION); - if (originalFolder.equals(info.getPath())) { + if (originalFolder.equals(info.getDataFolderPath())) { try { // The AVD is using the default data folder path based on the AVD name. // That folder needs to be adjusted to use the new name. @@ -1161,7 +1184,7 @@ public class Main { } File ini = info.getIniFile(); - if (ini.equals(AvdInfo.getIniFile(newName))) { + if (ini.equals(AvdInfo.getDefaultIniFile(avdManager, newName))) { errorAndExit("The AVD file '%s' is in the way.", ini.getCanonicalPath()); return; } diff --git a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java index 157e896..fb15cb5 100644 --- a/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java +++ b/sdkmanager/app/src/com/android/sdkmanager/SdkCommandLine.java @@ -38,47 +38,49 @@ class SdkCommandLine extends CommandLineProcessor { * or optional) for the given action. */ - public final static String VERB_LIST = "list"; - public final static String VERB_CREATE = "create"; - public final static String VERB_MOVE = "move"; - public final static String VERB_DELETE = "delete"; - public final static String VERB_UPDATE = "update"; - - public static final String OBJECT_SDK = "sdk"; - public static final String OBJECT_AVD = "avd"; - public static final String OBJECT_AVDS = "avds"; - public static final String OBJECT_TARGET = "target"; - public static final String OBJECT_TARGETS = "targets"; - public static final String OBJECT_PROJECT = "project"; - public static final String OBJECT_TEST_PROJECT = "test-project"; - public static final String OBJECT_LIB_PROJECT = "lib-project"; - public static final String OBJECT_EXPORT_PROJECT = "export-project"; - public static final String OBJECT_ADB = "adb"; - - public static final String ARG_ALIAS = "alias"; - public static final String ARG_ACTIVITY = "activity"; + public final static String VERB_LIST = "list"; //$NON-NLS-1$ + public final static String VERB_CREATE = "create"; //$NON-NLS-1$ + public final static String VERB_MOVE = "move"; //$NON-NLS-1$ + public final static String VERB_DELETE = "delete"; //$NON-NLS-1$ + public final static String VERB_UPDATE = "update"; //$NON-NLS-1$ + + public static final String OBJECT_SDK = "sdk"; //$NON-NLS-1$ + public static final String OBJECT_AVD = "avd"; //$NON-NLS-1$ + public static final String OBJECT_AVDS = "avds"; //$NON-NLS-1$ + public static final String OBJECT_TARGET = "target"; //$NON-NLS-1$ + public static final String OBJECT_TARGETS = "targets"; //$NON-NLS-1$ + public static final String OBJECT_PROJECT = "project"; //$NON-NLS-1$ + public static final String OBJECT_TEST_PROJECT = "test-project"; //$NON-NLS-1$ + public static final String OBJECT_LIB_PROJECT = "lib-project"; //$NON-NLS-1$ + public static final String OBJECT_EXPORT_PROJECT = "export-project"; //$NON-NLS-1$ + public static final String OBJECT_ADB = "adb"; //$NON-NLS-1$ + + public static final String ARG_ALIAS = "alias"; //$NON-NLS-1$ + public static final String ARG_ACTIVITY = "activity"; //$NON-NLS-1$ public static final String KEY_ACTIVITY = ARG_ACTIVITY; - public static final String KEY_PACKAGE = "package"; - public static final String KEY_MODE = "mode"; + public static final String KEY_PACKAGE = "package"; //$NON-NLS-1$ + public static final String KEY_MODE = "mode"; //$NON-NLS-1$ public static final String KEY_TARGET_ID = OBJECT_TARGET; - public static final String KEY_NAME = "name"; - public static final String KEY_LIBRARY = "library"; - public static final String KEY_PATH = "path"; - public static final String KEY_FILTER = "filter"; - public static final String KEY_SKIN = "skin"; - public static final String KEY_SDCARD = "sdcard"; - public static final String KEY_FORCE = "force"; - public static final String KEY_RENAME = "rename"; - public static final String KEY_SUBPROJECTS = "subprojects"; - public static final String KEY_MAIN_PROJECT = "main"; - public static final String KEY_NO_UI = "no-ui"; - public static final String KEY_NO_HTTPS = "no-https"; - public static final String KEY_PROXY_PORT = "proxy-port"; - public static final String KEY_PROXY_HOST = "proxy-host"; - public static final String KEY_DRY_MODE = "dry-mode"; - public static final String KEY_OBSOLETE = "obsolete"; - public static final String KEY_SNAPSHOT = "snapshot"; + public static final String KEY_NAME = "name"; //$NON-NLS-1$ + public static final String KEY_LIBRARY = "library"; //$NON-NLS-1$ + public static final String KEY_PATH = "path"; //$NON-NLS-1$ + public static final String KEY_FILTER = "filter"; //$NON-NLS-1$ + public static final String KEY_SKIN = "skin"; //$NON-NLS-1$ + public static final String KEY_SDCARD = "sdcard"; //$NON-NLS-1$ + public static final String KEY_FORCE = "force"; //$NON-NLS-1$ + public static final String KEY_RENAME = "rename"; //$NON-NLS-1$ + public static final String KEY_SUBPROJECTS = "subprojects"; //$NON-NLS-1$ + public static final String KEY_MAIN_PROJECT = "main"; //$NON-NLS-1$ + public static final String KEY_NO_UI = "no-ui"; //$NON-NLS-1$ + public static final String KEY_NO_HTTPS = "no-https"; //$NON-NLS-1$ + public static final String KEY_PROXY_PORT = "proxy-port"; //$NON-NLS-1$ + public static final String KEY_PROXY_HOST = "proxy-host"; //$NON-NLS-1$ + public static final String KEY_DRY_MODE = "dry-mode"; //$NON-NLS-1$ + public static final String KEY_OBSOLETE = "obsolete"; //$NON-NLS-1$ + public static final String KEY_SNAPSHOT = "snapshot"; //$NON-NLS-1$ + public static final String KEY_COMPACT = "compact"; //$NON-NLS-1$ + public static final String KEY_EOL_NULL = "null"; //$NON-NLS-1$ /** * Action definitions for SdkManager command line. @@ -149,117 +151,139 @@ class SdkCommandLine extends CommandLineProcessor { // The following defines the parameters of the actions defined in mAction. + // --- list avds --- + + define(Mode.BOOLEAN, false, + VERB_LIST, OBJECT_AVD, "c", KEY_COMPACT, //$NON-NLS-1$ + "Compact output (suitable for scripts)", false); + + define(Mode.BOOLEAN, false, + VERB_LIST, OBJECT_AVD, "0", KEY_EOL_NULL, //$NON-NLS-1$ + "Terminates lines with \\0 instead of \\n (e.g. for xargs -0). Only used by --" + KEY_COMPACT + ".", + false); + + // --- list targets --- + + define(Mode.BOOLEAN, false, + VERB_LIST, OBJECT_TARGET, "c", KEY_COMPACT, //$NON-NLS-1$ + "Compact output (suitable for scripts)", false); + + define(Mode.BOOLEAN, false, + VERB_LIST, OBJECT_TARGET, "0", KEY_EOL_NULL, //$NON-NLS-1$ + "Terminates lines with \\0 instead of \\n (e.g. for xargs -0) Only used by --" + KEY_COMPACT + ".", + false); + // --- create avd --- define(Mode.STRING, false, - VERB_CREATE, OBJECT_AVD, "p", KEY_PATH, + VERB_CREATE, OBJECT_AVD, "p", KEY_PATH, //$NON-NLS-1$ "Directory where the new AVD will be created", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_AVD, "n", KEY_NAME, + VERB_CREATE, OBJECT_AVD, "n", KEY_NAME, //$NON-NLS-1$ "Name of the new AVD", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_AVD, "t", KEY_TARGET_ID, + VERB_CREATE, OBJECT_AVD, "t", KEY_TARGET_ID, //$NON-NLS-1$ "Target ID of the new AVD", null); define(Mode.STRING, false, - VERB_CREATE, OBJECT_AVD, "s", KEY_SKIN, + VERB_CREATE, OBJECT_AVD, "s", KEY_SKIN, //$NON-NLS-1$ "Skin for the new AVD", null); define(Mode.STRING, false, - VERB_CREATE, OBJECT_AVD, "c", KEY_SDCARD, + VERB_CREATE, OBJECT_AVD, "c", KEY_SDCARD, //$NON-NLS-1$ "Path to a shared SD card image, or size of a new sdcard for the new AVD", null); define(Mode.BOOLEAN, false, - VERB_CREATE, OBJECT_AVD, "f", KEY_FORCE, + VERB_CREATE, OBJECT_AVD, "f", KEY_FORCE, //$NON-NLS-1$ "Forces creation (overwrites an existing AVD)", false); define(Mode.BOOLEAN, false, - VERB_CREATE, OBJECT_AVD, "a", KEY_SNAPSHOT, + VERB_CREATE, OBJECT_AVD, "a", KEY_SNAPSHOT, //$NON-NLS-1$ "Place a snapshots file in the AVD, to enable persistence.", false); // --- delete avd --- define(Mode.STRING, true, - VERB_DELETE, OBJECT_AVD, "n", KEY_NAME, + VERB_DELETE, OBJECT_AVD, "n", KEY_NAME, //$NON-NLS-1$ "Name of the AVD to delete", null); // --- move avd --- define(Mode.STRING, true, - VERB_MOVE, OBJECT_AVD, "n", KEY_NAME, + VERB_MOVE, OBJECT_AVD, "n", KEY_NAME, //$NON-NLS-1$ "Name of the AVD to move or rename", null); define(Mode.STRING, false, - VERB_MOVE, OBJECT_AVD, "r", KEY_RENAME, + VERB_MOVE, OBJECT_AVD, "r", KEY_RENAME, //$NON-NLS-1$ "New name of the AVD", null); define(Mode.STRING, false, - VERB_MOVE, OBJECT_AVD, "p", KEY_PATH, + VERB_MOVE, OBJECT_AVD, "p", KEY_PATH, //$NON-NLS-1$ "Path to the AVD's new directory", null); // --- update avd --- define(Mode.STRING, true, - VERB_UPDATE, OBJECT_AVD, "n", KEY_NAME, + VERB_UPDATE, OBJECT_AVD, "n", KEY_NAME, //$NON-NLS-1$ "Name of the AVD to update", null); // --- list sdk --- define(Mode.BOOLEAN, false, - VERB_LIST, OBJECT_SDK, "u", KEY_NO_UI, + VERB_LIST, OBJECT_SDK, "u", KEY_NO_UI, //$NON-NLS-1$ "Displays list result on console (no GUI)", true); define(Mode.BOOLEAN, false, - VERB_LIST, OBJECT_SDK, "s", KEY_NO_HTTPS, + VERB_LIST, OBJECT_SDK, "s", KEY_NO_HTTPS, //$NON-NLS-1$ "Uses HTTP instead of HTTPS (the default) for downloads", false); define(Mode.STRING, false, - VERB_LIST, OBJECT_SDK, "", KEY_PROXY_PORT, + VERB_LIST, OBJECT_SDK, "", KEY_PROXY_PORT, //$NON-NLS-1$ "HTTP/HTTPS proxy port (overrides settings if defined)", null); define(Mode.STRING, false, - VERB_LIST, OBJECT_SDK, "", KEY_PROXY_HOST, + VERB_LIST, OBJECT_SDK, "", KEY_PROXY_HOST, //$NON-NLS-1$ "HTTP/HTTPS proxy host (overrides settings if defined)", null); define(Mode.BOOLEAN, false, - VERB_LIST, OBJECT_SDK, "o", KEY_OBSOLETE, + VERB_LIST, OBJECT_SDK, "o", KEY_OBSOLETE, //$NON-NLS-1$ "Installs obsolete packages", false); // --- update sdk --- define(Mode.BOOLEAN, false, - VERB_UPDATE, OBJECT_SDK, "u", KEY_NO_UI, + VERB_UPDATE, OBJECT_SDK, "u", KEY_NO_UI, //$NON-NLS-1$ "Updates from command-line (does not display the GUI)", false); define(Mode.BOOLEAN, false, - VERB_UPDATE, OBJECT_SDK, "s", KEY_NO_HTTPS, + VERB_UPDATE, OBJECT_SDK, "s", KEY_NO_HTTPS, //$NON-NLS-1$ "Uses HTTP instead of HTTPS (the default) for downloads", false); define(Mode.STRING, false, - VERB_UPDATE, OBJECT_SDK, "", KEY_PROXY_PORT, + VERB_UPDATE, OBJECT_SDK, "", KEY_PROXY_PORT, //$NON-NLS-1$ "HTTP/HTTPS proxy port (overrides settings if defined)", null); define(Mode.STRING, false, - VERB_UPDATE, OBJECT_SDK, "", KEY_PROXY_HOST, + VERB_UPDATE, OBJECT_SDK, "", KEY_PROXY_HOST, //$NON-NLS-1$ "HTTP/HTTPS proxy host (overrides settings if defined)", null); define(Mode.BOOLEAN, false, - VERB_UPDATE, OBJECT_SDK, "f", KEY_FORCE, + VERB_UPDATE, OBJECT_SDK, "f", KEY_FORCE, //$NON-NLS-1$ "Forces replacement of a package or its parts, even if something has been modified", false); define(Mode.STRING, false, - VERB_UPDATE, OBJECT_SDK, "t", KEY_FILTER, + VERB_UPDATE, OBJECT_SDK, "t", KEY_FILTER, //$NON-NLS-1$ "A filter that limits the update to the specified types of packages in the form of\n" + "a comma-separated list of " + Arrays.toString(SdkRepoConstants.NODES), null); define(Mode.BOOLEAN, false, - VERB_UPDATE, OBJECT_SDK, "o", KEY_OBSOLETE, + VERB_UPDATE, OBJECT_SDK, "o", KEY_OBSOLETE, //$NON-NLS-1$ "Installs obsolete packages", false); define(Mode.BOOLEAN, false, - VERB_UPDATE, OBJECT_SDK, "n", KEY_DRY_MODE, + VERB_UPDATE, OBJECT_SDK, "n", KEY_DRY_MODE, //$NON-NLS-1$ "Simulates the update but does not download or install anything", false); @@ -269,7 +293,7 @@ class SdkCommandLine extends CommandLineProcessor { This currently does not work, the alias build rules need to be fixed. define(Mode.ENUM, true, - VERB_CREATE, OBJECT_PROJECT, "m", KEY_MODE, + VERB_CREATE, OBJECT_PROJECT, "m", KEY_MODE, //$NON-NLS-1$ "Project mode", new String[] { ARG_ACTIVITY, ARG_ALIAS }); */ define(Mode.STRING, true, @@ -277,46 +301,44 @@ class SdkCommandLine extends CommandLineProcessor { "p", KEY_PATH, "The new project's directory", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_PROJECT, "t", KEY_TARGET_ID, + VERB_CREATE, OBJECT_PROJECT, "t", KEY_TARGET_ID, //$NON-NLS-1$ "Target ID of the new project", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_PROJECT, "k", KEY_PACKAGE, + VERB_CREATE, OBJECT_PROJECT, "k", KEY_PACKAGE, //$NON-NLS-1$ "Android package name for the application", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_PROJECT, "a", KEY_ACTIVITY, + VERB_CREATE, OBJECT_PROJECT, "a", KEY_ACTIVITY, //$NON-NLS-1$ "Name of the default Activity that is created", null); define(Mode.STRING, false, - VERB_CREATE, OBJECT_PROJECT, "n", KEY_NAME, + VERB_CREATE, OBJECT_PROJECT, "n", KEY_NAME, //$NON-NLS-1$ "Project name", null); // --- create test-project --- define(Mode.STRING, true, - VERB_CREATE, OBJECT_TEST_PROJECT, - "p", KEY_PATH, + VERB_CREATE, OBJECT_TEST_PROJECT, "p", KEY_PATH, //$NON-NLS-1$ "The new project's directory", null); define(Mode.STRING, false, - VERB_CREATE, OBJECT_TEST_PROJECT, "n", KEY_NAME, + VERB_CREATE, OBJECT_TEST_PROJECT, "n", KEY_NAME, //$NON-NLS-1$ "Project name", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_TEST_PROJECT, "m", KEY_MAIN_PROJECT, + VERB_CREATE, OBJECT_TEST_PROJECT, "m", KEY_MAIN_PROJECT, //$NON-NLS-1$ "Path to directory of the app under test, relative to the test project directory", null); // --- create lib-project --- define(Mode.STRING, true, - VERB_CREATE, OBJECT_LIB_PROJECT, - "p", KEY_PATH, + VERB_CREATE, OBJECT_LIB_PROJECT, "p", KEY_PATH, //$NON-NLS-1$ "The new project's directory", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_LIB_PROJECT, "t", KEY_TARGET_ID, + VERB_CREATE, OBJECT_LIB_PROJECT, "t", KEY_TARGET_ID, //$NON-NLS-1$ "Target ID of the new project", null); define(Mode.STRING, false, - VERB_CREATE, OBJECT_LIB_PROJECT, "n", KEY_NAME, + VERB_CREATE, OBJECT_LIB_PROJECT, "n", KEY_NAME, //$NON-NLS-1$ "Project name", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_LIB_PROJECT, "k", KEY_PACKAGE, + VERB_CREATE, OBJECT_LIB_PROJECT, "k", KEY_PACKAGE, //$NON-NLS-1$ "Android package name for the library", null); // --- create export-project --- @@ -324,75 +346,63 @@ class SdkCommandLine extends CommandLineProcessor { * disabled until the feature is officially supported. define(Mode.STRING, true, - VERB_CREATE, OBJECT_EXPORT_PROJECT, - "p", KEY_PATH, + VERB_CREATE, OBJECT_EXPORT_PROJECT, "p", KEY_PATH, //$NON-NLS-1$ "Location path of new project", null); define(Mode.STRING, false, - VERB_CREATE, OBJECT_EXPORT_PROJECT, "n", KEY_NAME, + VERB_CREATE, OBJECT_EXPORT_PROJECT, "n", KEY_NAME, //$NON-NLS-1$ "Project name", null); define(Mode.STRING, true, - VERB_CREATE, OBJECT_EXPORT_PROJECT, "k", KEY_PACKAGE, + VERB_CREATE, OBJECT_EXPORT_PROJECT, "k", KEY_PACKAGE, //$NON-NLS-1$ "Package name", null); */ // --- update project --- define(Mode.STRING, true, - VERB_UPDATE, OBJECT_PROJECT, - "p", KEY_PATH, + VERB_UPDATE, OBJECT_PROJECT, "p", KEY_PATH, //$NON-NLS-1$ "The project's directory", null); define(Mode.STRING, false, - VERB_UPDATE, OBJECT_PROJECT, - "t", KEY_TARGET_ID, + VERB_UPDATE, OBJECT_PROJECT, "t", KEY_TARGET_ID, //$NON-NLS-1$ "Target ID to set for the project", null); define(Mode.STRING, false, - VERB_UPDATE, OBJECT_PROJECT, - "n", KEY_NAME, + VERB_UPDATE, OBJECT_PROJECT, "n", KEY_NAME, //$NON-NLS-1$ "Project name", null); define(Mode.BOOLEAN, false, - VERB_UPDATE, OBJECT_PROJECT, - "s", KEY_SUBPROJECTS, + VERB_UPDATE, OBJECT_PROJECT, "s", KEY_SUBPROJECTS, //$NON-NLS-1$ "Also updates any projects in sub-folders, such as test projects.", false); define(Mode.STRING, false, - VERB_UPDATE, OBJECT_PROJECT, - "l", KEY_LIBRARY, + VERB_UPDATE, OBJECT_PROJECT, "l", KEY_LIBRARY, //$NON-NLS-1$ "Directory of an Android library to add, relative to this project's directory", null); // --- update test project --- define(Mode.STRING, true, - VERB_UPDATE, OBJECT_TEST_PROJECT, - "p", KEY_PATH, + VERB_UPDATE, OBJECT_TEST_PROJECT, "p", KEY_PATH, //$NON-NLS-1$ "The project's directory", null); define(Mode.STRING, true, - VERB_UPDATE, OBJECT_TEST_PROJECT, - "m", KEY_MAIN_PROJECT, + VERB_UPDATE, OBJECT_TEST_PROJECT, "m", KEY_MAIN_PROJECT, //$NON-NLS-1$ "Directory of the app under test, relative to the test project directory", null); // --- update lib project --- define(Mode.STRING, true, - VERB_UPDATE, OBJECT_LIB_PROJECT, - "p", KEY_PATH, + VERB_UPDATE, OBJECT_LIB_PROJECT, "p", KEY_PATH, //$NON-NLS-1$ "The project's directory", null); define(Mode.STRING, false, - VERB_UPDATE, OBJECT_LIB_PROJECT, - "t", KEY_TARGET_ID, + VERB_UPDATE, OBJECT_LIB_PROJECT, "t", KEY_TARGET_ID, //$NON-NLS-1$ "Target ID to set for the project", null); // --- update export project --- /* * disabled until the feature is officially supported. define(Mode.STRING, true, - VERB_UPDATE, OBJECT_EXPORT_PROJECT, - "p", KEY_PATH, + VERB_UPDATE, OBJECT_EXPORT_PROJECT, "p", KEY_PATH, //$NON-NLS-1$ "Location path of the project", null); define(Mode.STRING, false, - VERB_UPDATE, OBJECT_EXPORT_PROJECT, - "n", KEY_NAME, + VERB_UPDATE, OBJECT_EXPORT_PROJECT, "n", KEY_NAME, //$NON-NLS-1$ "Project name", null); define(Mode.BOOLEAN, false, - VERB_UPDATE, OBJECT_EXPORT_PROJECT, "f", KEY_FORCE, + VERB_UPDATE, OBJECT_EXPORT_PROJECT, "f", KEY_FORCE, //$NON-NLS-1$ "Force replacing the build.xml file", false); */ } @@ -527,4 +537,16 @@ class SdkCommandLine extends CommandLineProcessor { public String getParamProxyPort() { return ((String) getValue(null, null, KEY_PROXY_PORT)); } + + // -- some helpers for list avds and list targets flags + + /** Helper to retrieve the --compact value. */ + public boolean getFlagCompact() { + return ((Boolean) getValue(null, null, KEY_COMPACT)).booleanValue(); + } + + /** Helper to retrieve the --null value. */ + public boolean getFlagEolNull() { + return ((Boolean) getValue(null, null, KEY_EOL_NULL)).booleanValue(); + } } diff --git a/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java b/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java index 9579bf5..a5a8289 100644 --- a/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java +++ b/sdkmanager/app/tests/com/android/sdkmanager/AvdManagerTest.java @@ -16,49 +16,36 @@ package com.android.sdkmanager; -import static java.io.File.createTempFile; - import com.android.io.FileWrapper; import com.android.sdklib.IAndroidTarget; -import com.android.sdklib.SdkManager; -import com.android.sdklib.internal.avd.AvdManager; -import com.android.sdklib.internal.project.ProjectProperties; -import com.android.sdklib.mock.MockLog; import com.android.sdklib.SdkConstants; +import com.android.sdklib.internal.avd.AvdInfo; +import com.android.sdklib.internal.project.ProjectProperties; import java.io.File; import java.util.Map; -import junit.framework.TestCase; - -public class AvdManagerTest extends TestCase { +public class AvdManagerTest extends SdkManagerTestCase { - private AvdManager mAvdManager; - private SdkManager mSdkManager; - private MockLog mLog; - private File mFakeSdk; - private File mAvdFolder; private IAndroidTarget mTarget; + private File mAvdFolder; @Override public void setUp() throws Exception { - mLog = new MockLog(); - mFakeSdk = SdkManagerTestUtil.makeFakeSdk(createTempFile(this.getClass().getSimpleName(), null)); - mSdkManager = SdkManager.createManager(mFakeSdk.getAbsolutePath(), mLog); - assertNotNull("sdkManager location was invalid", mSdkManager); + super.setUp(); - mAvdManager = new AvdManager(mSdkManager, mLog); - mAvdFolder = new File(mFakeSdk, "avdData"); - mTarget = mSdkManager.getTargets()[0]; + mTarget = getSdkManager().getTargets()[0]; + mAvdFolder = AvdInfo.getDefaultAvdFolder(getAvdManager(), getName()); } @Override public void tearDown() throws Exception { - SdkManagerTestUtil.deleteDir(mFakeSdk); + super.tearDown(); } public void testCreateAvdWithoutSnapshot() { - mAvdManager.createAvd( + + getAvdManager().createAvd( mAvdFolder, this.getName(), mTarget, @@ -69,17 +56,17 @@ public class AvdManagerTest extends TestCase { false, // createSnapshot false, // removePrevious false, // editExisting - mLog); + getLog()); assertEquals("[P Created AVD '" + this.getName() + "' based on Android 0.0, ARM (armeabi) processor\n]", - mLog.toString()); + getLog().toString()); assertTrue("Expected config.ini in " + mAvdFolder, new File(mAvdFolder, "config.ini").exists()); Map<String, String> map = ProjectProperties.parsePropertyFile( - new FileWrapper(mAvdFolder, "config.ini"), mLog); + new FileWrapper(mAvdFolder, "config.ini"), getLog()); assertEquals("HVGA", map.get("skin.name")); - assertEquals("platforms/v0_0/skins/HVGA", map.get("skin.path")); - assertEquals("platforms/v0_0/images/", map.get("image.sysdir.1")); + assertEquals("platforms/v0_0/skins/HVGA", map.get("skin.path").replace(File.separatorChar, '/')); + assertEquals("platforms/v0_0/images/", map.get("image.sysdir.1").replace(File.separatorChar, '/')); assertEquals(null, map.get("snapshot.present")); assertTrue("Expected userdata.img in " + mAvdFolder, new File(mAvdFolder, "userdata.img").exists()); @@ -89,7 +76,7 @@ public class AvdManagerTest extends TestCase { public void testCreateAvdWithSnapshot() { - mAvdManager.createAvd( + getAvdManager().createAvd( mAvdFolder, this.getName(), mTarget, @@ -100,14 +87,14 @@ public class AvdManagerTest extends TestCase { true, // createSnapshot false, // removePrevious false, // editExisting - mLog); + getLog()); assertEquals("[P Created AVD '" + this.getName() + "' based on Android 0.0, ARM (armeabi) processor\n]", - mLog.toString()); + getLog().toString()); assertTrue("Expected snapshots.img in " + mAvdFolder, new File(mAvdFolder, "snapshots.img").exists()); Map<String, String> map = ProjectProperties.parsePropertyFile( - new FileWrapper(mAvdFolder, "config.ini"), mLog); + new FileWrapper(mAvdFolder, "config.ini"), getLog()); assertEquals("true", map.get("snapshot.present")); } } diff --git a/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java b/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java index 3acb01a..4a17e32 100644 --- a/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java +++ b/sdkmanager/app/tests/com/android/sdkmanager/MainTest.java @@ -19,9 +19,7 @@ package com.android.sdkmanager; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkConstants; -import com.android.sdklib.SdkManager; -import com.android.sdklib.internal.avd.AvdManager; -import com.android.sdklib.mock.MockLog; +import com.android.sdklib.internal.avd.AvdInfo; import com.android.sdklib.repository.SdkAddonConstants; import com.android.sdklib.repository.SdkRepoConstants; import com.android.util.Pair; @@ -32,49 +30,36 @@ import java.util.Arrays; import java.util.Set; import java.util.TreeSet; -import junit.framework.TestCase; +public class MainTest extends SdkManagerTestCase { -public class MainTest extends TestCase { - - private File mFakeSdk; - private MockLog mLog; - private SdkManager mSdkManager; - private AvdManager mAvdManager; - private File mAvdFolder; private IAndroidTarget mTarget; - private File fakeSdkDir; + private File mAvdFolder; @Override public void setUp() throws Exception { - mLog = new MockLog(); - fakeSdkDir = File.createTempFile( - this.getClass().getSimpleName() + '_' + this.getName(), null); - mFakeSdk = SdkManagerTestUtil.makeFakeSdk(fakeSdkDir); - mSdkManager = SdkManager.createManager(mFakeSdk.getAbsolutePath(), mLog); - assertNotNull("sdkManager location was invalid", mSdkManager); - - mAvdManager = new AvdManager(mSdkManager, mLog); - mAvdFolder = new File(mFakeSdk, "avdData"); - mTarget = mSdkManager.getTargets()[0]; + super.setUp(); + + mTarget = getSdkManager().getTargets()[0]; + mAvdFolder = AvdInfo.getDefaultAvdFolder(getAvdManager(), getName()); } @Override public void tearDown() throws Exception { - SdkManagerTestUtil.deleteDir(mFakeSdk); + super.tearDown(); } public void testDisplayEmptyAvdList() { Main main = new Main(); - main.setLogger(mLog); - mLog.clear(); - main.displayAvdList(mAvdManager); - assertEquals("P Available Android Virtual Devices:\n", mLog.toString()); + main.setLogger(getLog()); + getLog().clear(); + main.displayAvdList(getAvdManager()); + assertEquals("[P Available Android Virtual Devices:\n]", getLog().toString()); } public void testDisplayAvdListOfOneNonSnapshot() { Main main = new Main(); - main.setLogger(mLog); - mAvdManager.createAvd( + main.setLogger(getLog()); + getAvdManager().createAvd( mAvdFolder, this.getName(), mTarget, @@ -85,10 +70,10 @@ public class MainTest extends TestCase { false, // createSnapshot false, // removePrevious false, // editExisting - mLog); + getLog()); - mLog.clear(); - main.displayAvdList(mAvdManager); + getLog().clear(); + main.displayAvdList(getAvdManager()); assertEquals( "[P Available Android Virtual Devices:\n" + ", P Name: " + this.getName() + "\n" @@ -96,14 +81,14 @@ public class MainTest extends TestCase { + ", P Target: Android 0.0 (API level 0)\n" + ", P Skin: HVGA\n" + "]", - mLog.toString()); + getLog().toString()); } public void testDisplayAvdListOfOneSnapshot() { Main main = new Main(); - main.setLogger(mLog); + main.setLogger(getLog()); - mAvdManager.createAvd( + getAvdManager().createAvd( mAvdFolder, this.getName(), mTarget, @@ -114,10 +99,10 @@ public class MainTest extends TestCase { true, // createSnapshot false, // removePrevious false, // editExisting - mLog); + getLog()); - mLog.clear(); - main.displayAvdList(mAvdManager); + getLog().clear(); + main.displayAvdList(getAvdManager()); assertEquals( "[P Available Android Virtual Devices:\n" + ", P Name: " + this.getName() + "\n" @@ -126,7 +111,7 @@ public class MainTest extends TestCase { + ", P Skin: HVGA\n" + ", P Snapshot: true\n" + "]", - mLog.toString()); + getLog().toString()); } public void testCheckFilterValues() { @@ -178,7 +163,7 @@ public class MainTest extends TestCase { // Finally check that checkFilterValues accepts all these values, one by one. Main main = new Main(); - main.setLogger(mLog); + main.setLogger(getLog()); for (int step = 0; step < 3; step++) { for (String value : expectedValues) { diff --git a/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestCase.java b/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestCase.java new file mode 100755 index 0000000..9fdd852 --- /dev/null +++ b/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestCase.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2011 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.sdkmanager; + + +import com.android.prefs.AndroidLocation; +import com.android.prefs.AndroidLocation.AndroidLocationException; +import com.android.sdklib.ISdkLog; +import com.android.sdklib.SdkConstants; +import com.android.sdklib.SdkManager; +import com.android.sdklib.internal.avd.AvdManager; +import com.android.sdklib.mock.MockLog; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import junit.framework.TestCase; + +/** + * Test case that allocates a temporary SDK, a temporary AVD base folder + * with an SdkManager and an AvdManager that points to them. + */ +public abstract class SdkManagerTestCase extends TestCase { + + private File mFakeSdk; + private MockLog mLog; + private SdkManager mSdkManager; + private TmpAvdManager mAvdManager; + + /** Returns the {@link MockLog} for this test case. */ + public MockLog getLog() { + return mLog; + } + + /** Returns the {@link SdkManager} for this test case. */ + public SdkManager getSdkManager() { + return mSdkManager; + } + + /** Returns the {@link AvdManager} for this test case. */ + public TmpAvdManager getAvdManager() { + return mAvdManager; + } + + /** + * Sets up a {@link MockLog}, a fake SDK in a temporary directory + * and an AVD Manager pointing to an initially-empty AVD directory. + */ + @Override + public void setUp() throws Exception { + mLog = new MockLog(); + mFakeSdk = makeFakeSdk(); + mSdkManager = SdkManager.createManager(mFakeSdk.getAbsolutePath(), mLog); + assertNotNull("SdkManager location was invalid", mSdkManager); + + mAvdManager = new TmpAvdManager(mSdkManager, mLog); + } + + /** + * Removes the temporary SDK and AVD directories. + */ + @Override + public void tearDown() throws Exception { + deleteDir(mFakeSdk); + } + + /** + * An {@link AvdManager} that uses a temporary directory + * located <em>inside</em> the SDK directory for testing. + * The AVD list should be initially empty. + */ + protected static class TmpAvdManager extends AvdManager { + + /* + * Implementation detail: + * - When the super.AvdManager constructor is invoked, it will invoke + * the buildAvdFilesList() to fill the initial AVD list, which will in + * turn call getBaseAvdFolder(). + * - That's why mTmpAvdRoot is initialized in getAvdRoot() rather than + * in the constructor, since we can't initialize fields before the super() + * call. + */ + + /** + * AVD Root, initialized "lazily" when the AVD root is first requested. + */ + private File mTmpAvdRoot; + + public TmpAvdManager(SdkManager sdkManager, ISdkLog log) throws AndroidLocationException { + super(sdkManager, log); + } + + @Override + public String getBaseAvdFolder() throws AndroidLocationException { + if (mTmpAvdRoot == null) { + mTmpAvdRoot = new File(getSdkManager().getLocation(), "tmp_avds"); + mTmpAvdRoot.mkdirs(); + } + return mTmpAvdRoot.getAbsolutePath(); + } + } + + /** + * Build enough of a skeleton SDK to make the tests pass. + * <p/> + * Ideally this wouldn't touch the file system but the current + * structure of the SdkManager and AvdManager makes this difficult. + * + * @return Path to the temporary SDK root + * @throws IOException + */ + private File makeFakeSdk() throws IOException { + + File tmpFile = File.createTempFile( + this.getClass().getSimpleName() + '_' + this.getName(), null); + tmpFile.delete(); + tmpFile.mkdirs(); + + AndroidLocation.resetFolder(); + System.setProperty("user.home", tmpFile.getAbsolutePath()); + File addonsDir = new File(tmpFile, SdkConstants.FD_ADDONS); + addonsDir.mkdir(); + File toolsLibEmuDir = new File(tmpFile, SdkConstants.OS_SDK_TOOLS_LIB_FOLDER + "emulator"); + toolsLibEmuDir.mkdirs(); + new File(toolsLibEmuDir, "snapshots.img").createNewFile(); + File platformsDir = new File(tmpFile, SdkConstants.FD_PLATFORMS); + + // Creating a fake target here on down + File targetDir = new File(platformsDir, "v0_0"); + targetDir.mkdirs(); + new File(targetDir, SdkConstants.FN_FRAMEWORK_LIBRARY).createNewFile(); + new File(targetDir, SdkConstants.FN_FRAMEWORK_AIDL).createNewFile(); + new File(targetDir, SdkConstants.FN_SOURCE_PROP).createNewFile(); + File buildProp = new File(targetDir, SdkConstants.FN_BUILD_PROP); + FileWriter out = new FileWriter(buildProp); + out.write(SdkManager.PROP_VERSION_RELEASE + "=0.0\n"); + out.write(SdkManager.PROP_VERSION_SDK + "=0\n"); + out.write(SdkManager.PROP_VERSION_CODENAME + "=REL\n"); + out.close(); + File imagesDir = new File(targetDir, "images"); + imagesDir.mkdirs(); + new File(imagesDir, "userdata.img").createNewFile(); + File skinsDir = new File(targetDir, "skins"); + File hvgaDir = new File(skinsDir, "HVGA"); + hvgaDir.mkdirs(); + return tmpFile; + } + + /** + * Recursive delete directory. Mostly for fake SDKs. + * + * @param root directory to delete + */ + private void deleteDir(File root) { + if (root.exists()) { + for (File file : root.listFiles()) { + if (file.isDirectory()) { + deleteDir(file); + } else { + file.delete(); + } + } + root.delete(); + } + } + +} diff --git a/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestUtil.java b/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestUtil.java deleted file mode 100644 index 96efb5c..0000000 --- a/sdkmanager/app/tests/com/android/sdkmanager/SdkManagerTestUtil.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2008 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.sdkmanager; - -import com.android.prefs.AndroidLocation; -import com.android.sdklib.SdkConstants; -import com.android.sdklib.SdkManager; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; - -public class SdkManagerTestUtil { - /** - * Build enough of a skeleton SDK to make the tests pass. - *<p> - * Ideally this wouldn't touch the file system, but I'm not inclined to - * fiddle around with mock file systems just at the moment. - * - * @return an sdk manager to a fake sdk - * @throws IOException - */ - public static File makeFakeSdk(File fakeSdk) throws IOException { - fakeSdk.delete(); - fakeSdk.mkdirs(); - AndroidLocation.resetFolder(); - System.setProperty("user.home", fakeSdk.getAbsolutePath()); - File addonsDir = new File(fakeSdk, SdkConstants.FD_ADDONS); - addonsDir.mkdir(); - File toolsLibEmuDir = new File(fakeSdk, SdkConstants.OS_SDK_TOOLS_LIB_FOLDER + "emulator"); - toolsLibEmuDir.mkdirs(); - new File(toolsLibEmuDir, "snapshots.img").createNewFile(); - File platformsDir = new File(fakeSdk, SdkConstants.FD_PLATFORMS); - - // Creating a fake target here on down - File targetDir = new File(platformsDir, "v0_0"); - targetDir.mkdirs(); - new File(targetDir, SdkConstants.FN_FRAMEWORK_LIBRARY).createNewFile(); - new File(targetDir, SdkConstants.FN_FRAMEWORK_AIDL).createNewFile(); - new File(targetDir, SdkConstants.FN_SOURCE_PROP).createNewFile(); - File buildProp = new File(targetDir, SdkConstants.FN_BUILD_PROP); - FileWriter out = new FileWriter(buildProp); - out.write(SdkManager.PROP_VERSION_RELEASE + "=0.0\n"); - out.write(SdkManager.PROP_VERSION_SDK + "=0\n"); - out.write(SdkManager.PROP_VERSION_CODENAME + "=REL\n"); - out.close(); - File imagesDir = new File(targetDir, "images"); - imagesDir.mkdirs(); - new File(imagesDir, "userdata.img").createNewFile(); - File skinsDir = new File(targetDir, "skins"); - File hvgaDir = new File(skinsDir, "HVGA"); - hvgaDir.mkdirs(); - return fakeSdk; - } - - /** - * Recursive delete directory. Mostly for fake SDKs. - * - * @param root directory to delete - */ - public static void deleteDir(File root) { - if (root.exists()) { - for (File file : root.listFiles()) { - if (file.isDirectory()) { - deleteDir(file); - } else { - file.delete(); - } - } - root.delete(); - } - } - -} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdInfo.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdInfo.java new file mode 100755 index 0000000..81ffa5d --- /dev/null +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdInfo.java @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.sdklib.internal.avd; + +import com.android.prefs.AndroidLocation.AndroidLocationException; +import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.SdkConstants; + +import java.io.File; +import java.util.Collections; +import java.util.Map; + +/** + * An immutable structure describing an Android Virtual Device. + */ +public final class AvdInfo implements Comparable<AvdInfo> { + + /** + * Status for an {@link AvdInfo}. Indicates whether or not this AVD is valid. + */ + public static enum AvdStatus { + /** No error */ + OK, + /** Missing 'path' property in the ini file */ + ERROR_PATH, + /** Missing config.ini file in the AVD data folder */ + ERROR_CONFIG, + /** Missing 'target' property in the ini file */ + ERROR_TARGET_HASH, + /** Target was not resolved from its hash */ + ERROR_TARGET, + /** Unable to parse config.ini */ + ERROR_PROPERTIES, + /** System Image folder in config.ini doesn't exist */ + ERROR_IMAGE_DIR; + } + + private final String mName; + private final File mIniFile; + private final String mFolderPath; + private final String mTargetHash; + private final IAndroidTarget mTarget; + private final String mAbiType; + private final Map<String, String> mProperties; + private final AvdStatus mStatus; + + /** + * Creates a new valid AVD info. Values are immutable. + * <p/> + * Such an AVD is available and can be used. + * The error string is set to null. + * + * @param name The name of the AVD (for display or reference) + * @param iniFile The path to the config.ini file + * @param folderPath The path to the data directory + * @param targetHash the target hash + * @param target The target. Can be null, if the target was not resolved. + * @param abiType Name of the abi. + * @param properties The property map. Cannot be null. + */ + public AvdInfo(String name, + File iniFile, + String folderPath, + String targetHash, + IAndroidTarget target, + String abiType, + Map<String, String> properties) { + this(name, iniFile, folderPath, targetHash, target, abiType, properties, AvdStatus.OK); + } + + /** + * Creates a new <em>invalid</em> AVD info. Values are immutable. + * <p/> + * Such an AVD is not complete and cannot be used. + * The error string must be non-null. + * + * @param name The name of the AVD (for display or reference) + * @param iniFile The path to the config.ini file + * @param folderPath The path to the data directory + * @param targetHash the target hash + * @param target The target. Can be null, if the target was not resolved. + * @param abiType Name of the abi. + * @param properties The property map. Can be null. + * @param status The {@link AvdStatus} of this AVD. Cannot be null. + */ + public AvdInfo(String name, + File iniFile, + String folderPath, + String targetHash, + IAndroidTarget target, + String abiType, + Map<String, String> properties, + AvdStatus status) { + mName = name; + mIniFile = iniFile; + mFolderPath = folderPath; + mTargetHash = targetHash; + mTarget = target; + mAbiType = abiType; + mProperties = properties == null ? null : Collections.unmodifiableMap(properties); + mStatus = status; + } + + /** Returns the name of the AVD. */ + public String getName() { + return mName; + } + + /** Returns the path of the AVD data directory. */ + public String getDataFolderPath() { + return mFolderPath; + } + + /** Returns the processor type of the AVD. */ + public String getAbiType() { + return mAbiType; + } + + /** Convenience function to return a more user friendly name of the abi type. */ + public static String getPrettyAbiType(String raw) { + String s = null; + if (raw.equalsIgnoreCase(SdkConstants.ABI_ARMEABI)) { + s = "ARM (" + SdkConstants.ABI_ARMEABI + ")"; + } + else if (raw.equalsIgnoreCase(SdkConstants.ABI_INTEL_ATOM)) { + s = "Intel Atom (" + SdkConstants.ABI_INTEL_ATOM + ")"; + } + else { + s = raw + " (" + raw + ")"; + } + return s; + } + + /** + * Returns the emulator executable path + * @param sdkPath path of the sdk + * @return path of the emulator executable + */ + public String getEmulatorPath(String sdkPath) { + String path = sdkPath + SdkConstants.OS_SDK_TOOLS_FOLDER; + + // Start with base name of the emulator + path = path + SdkConstants.FN_EMULATOR; + + // If not using ARM, add processor type to emulator command line + boolean useAbi = !getAbiType().equalsIgnoreCase(SdkConstants.ABI_ARMEABI); + + if (useAbi) { + path = path + "-" + getAbiType(); //$NON-NLS-1$ + } + // Add OS appropriate emulator extension (e.g., .exe on windows) + path = path + SdkConstants.FN_EMULATOR_EXTENSION; + + // HACK: The AVD manager should look for "emulator" or for "emulator-abi" (if not arm). + // However this is a transition period and we don't have that unified "emulator" binary + // in AOSP so if we can't find the generic one, look for an abi-specific one with the + // special case that the armeabi one is actually named emulator-arm. + // TODO remove this kludge once no longer necessary. + if (!useAbi && !(new File(path).isFile())) { + path = sdkPath + SdkConstants.OS_SDK_TOOLS_FOLDER; + path = path + SdkConstants.FN_EMULATOR; + path = path + "-" //$NON-NLS-1$ + + getAbiType().replace(SdkConstants.ABI_ARMEABI, "arm"); //$NON-NLS-1$ + path = path + SdkConstants.FN_EMULATOR_EXTENSION; + } + + return path; + } + + /** + * Returns the target hash string. + */ + public String getTargetHash() { + return mTargetHash; + } + + /** Returns the target of the AVD, or <code>null</code> if it has not been resolved. */ + public IAndroidTarget getTarget() { + return mTarget; + } + + /** Returns the {@link AvdStatus} of the receiver. */ + public AvdStatus getStatus() { + return mStatus; + } + + /** + * Helper method that returns the default AVD folder that would be used for a given + * AVD name <em>if and only if</em> the AVD was created with the default choice. + * <p/> + * Callers must NOT use this to "guess" the actual folder from an actual AVD since + * the purpose of the AVD .ini file is to be able to change this folder. Callers + * should however use this to create a new {@link AvdInfo} to setup its data folder + * to the default. + * <p/> + * The default is {@code getDefaultAvdFolder()/avdname.avd/}. + * <p/> + * For an actual existing AVD, callers must use {@link #getDataFolderPath()} instead. + * + * @param manager The AVD Manager, used to get the AVD storage path. + * @param avdName The name of the desired AVD. + * @throws AndroidLocationException if there's a problem getting android root directory. + */ + public static File getDefaultAvdFolder(AvdManager manager, String avdName) + throws AndroidLocationException { + return new File(manager.getBaseAvdFolder(), + avdName + AvdManager.AVD_FOLDER_EXTENSION); + } + + /** + * Helper method that returns the .ini {@link File} for a given AVD name. + * <p/> + * The default is {@code getDefaultAvdFolder()/avdname.ini}. + * + * @param manager The AVD Manager, used to get the AVD storage path. + * @param avdName The name of the desired AVD. + * @throws AndroidLocationException if there's a problem getting android root directory. + */ + public static File getDefaultIniFile(AvdManager manager, String avdName) + throws AndroidLocationException { + String avdRoot = manager.getBaseAvdFolder(); + return new File(avdRoot, avdName + AvdManager.INI_EXTENSION); + } + + /** + * Returns the .ini {@link File} for this AVD. + */ + public File getIniFile() { + return mIniFile; + } + + /** + * Helper method that returns the Config {@link File} for a given AVD name. + */ + public static File getConfigFile(String path) { + return new File(path, AvdManager.CONFIG_INI); + } + + /** + * Returns the Config {@link File} for this AVD. + */ + public File getConfigFile() { + return getConfigFile(mFolderPath); + } + + /** + * Returns an unmodifiable map of properties for the AVD. This can be null. + */ + public Map<String, String> getProperties() { + return mProperties; + } + + /** + * Returns the error message for the AVD or <code>null</code> if {@link #getStatus()} + * returns {@link AvdStatus#OK} + */ + public String getErrorMessage() { + switch (mStatus) { + case ERROR_PATH: + return String.format("Missing AVD 'path' property in %1$s", getIniFile()); + case ERROR_CONFIG: + return String.format("Missing config.ini file in %1$s", mFolderPath); + case ERROR_TARGET_HASH: + return String.format("Missing 'target' property in %1$s", getIniFile()); + case ERROR_TARGET: + return String.format("Unknown target '%1$s' in %2$s", + mTargetHash, getIniFile()); + case ERROR_PROPERTIES: + return String.format("Failed to parse properties from %1$s", + getConfigFile()); + case ERROR_IMAGE_DIR: + return String.format( + "Invalid value in image.sysdir. Run 'android update avd -n %1$s'", + mName); + case OK: + assert false; + return null; + } + + return null; + } + + /** + * Returns whether an emulator is currently running the AVD. + */ + public boolean isRunning() { + File f = new File(mFolderPath, "userdata-qemu.img.lock"); //$NON-NLS-1$ + return f.isFile(); + } + + /** + * Compares this object with the specified object for order. Returns a + * negative integer, zero, or a positive integer as this object is less + * than, equal to, or greater than the specified object. + * + * @param o the Object to be compared. + * @return a negative integer, zero, or a positive integer as this object is + * less than, equal to, or greater than the specified object. + */ + public int compareTo(AvdInfo o) { + // first handle possible missing targets (if the AVD failed to load for unresolved targets) + if (mTarget == null && o != null && o.mTarget == null) { + return 0; + } if (mTarget == null) { + return +1; + } else if (o == null || o.mTarget == null) { + return -1; + } + + // then compare the targets + int targetDiff = mTarget.compareTo(o.mTarget); + + if (targetDiff == 0) { + // same target? compare on the avd name + return mName.compareTo(o.mName); + } + + return targetDiff; + } +} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java index 627bdb9..c9a3561 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java @@ -23,7 +23,7 @@ import com.android.sdklib.IAndroidTarget; import com.android.sdklib.ISdkLog; import com.android.sdklib.SdkConstants; import com.android.sdklib.SdkManager; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo.AvdStatus; +import com.android.sdklib.internal.avd.AvdInfo.AvdStatus; import com.android.sdklib.internal.project.ProjectProperties; import com.android.util.Pair; @@ -38,7 +38,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -48,7 +47,7 @@ import java.util.regex.Pattern; /** * Android Virtual Device Manager to manage AVDs. */ -public final class AvdManager { +public class AvdManager { /** * Exception thrown when something is wrong with a target path. @@ -132,11 +131,11 @@ public final class AvdManager { public final static Pattern NUMERIC_SKIN_SIZE = Pattern.compile("([0-9]{2,})x([0-9]{2,})"); //$NON-NLS-1$ private final static String USERDATA_IMG = "userdata.img"; //$NON-NLS-1$ - private final static String CONFIG_INI = "config.ini"; //$NON-NLS-1$ + final static String CONFIG_INI = "config.ini"; //$NON-NLS-1$ private final static String SDCARD_IMG = "sdcard.img"; //$NON-NLS-1$ private final static String SNAPSHOTS_IMG = "snapshots.img"; //$NON-NLS-1$ - private final static String INI_EXTENSION = ".ini"; //$NON-NLS-1$ + final static String INI_EXTENSION = ".ini"; //$NON-NLS-1$ private final static Pattern INI_NAME_PATTERN = Pattern.compile("(.+)\\" + //$NON-NLS-1$ INI_EXTENSION + "$", //$NON-NLS-1$ Pattern.CASE_INSENSITIVE); @@ -192,305 +191,12 @@ public final class AvdManager { CONFLICT_EXISTING_PATH, } - /** - * An immutable structure describing an Android Virtual Device. - */ - public static final class AvdInfo implements Comparable<AvdInfo> { - - /** - * Status for an {@link AvdInfo}. Indicates whether or not this AVD is valid. - */ - public static enum AvdStatus { - /** No error */ - OK, - /** Missing 'path' property in the ini file */ - ERROR_PATH, - /** Missing config.ini file in the AVD data folder */ - ERROR_CONFIG, - /** Missing 'target' property in the ini file */ - ERROR_TARGET_HASH, - /** Target was not resolved from its hash */ - ERROR_TARGET, - /** Unable to parse config.ini */ - ERROR_PROPERTIES, - /** System Image folder in config.ini doesn't exist */ - ERROR_IMAGE_DIR; - } - - private final String mName; - private final String mPath; - private final String mTargetHash; - private final IAndroidTarget mTarget; - private final String mAbiType; - private final Map<String, String> mProperties; - private final AvdStatus mStatus; - - /** - * Creates a new valid AVD info. Values are immutable. - * <p/> - * Such an AVD is available and can be used. - * The error string is set to null. - * - * @param name The name of the AVD (for display or reference) - * @param path The path to the config.ini file - * @param targetHash the target hash - * @param target The target. Can be null, if the target was not resolved. - * @param abiType Name of the abi. - * @param properties The property map. Cannot be null. - */ - public AvdInfo(String name, String path, String targetHash, IAndroidTarget target, - String abiType, Map<String, String> properties) { - this(name, path, targetHash, target, abiType, properties, AvdStatus.OK); - } - - /** - * Creates a new <em>invalid</em> AVD info. Values are immutable. - * <p/> - * Such an AVD is not complete and cannot be used. - * The error string must be non-null. - * - * @param name The name of the AVD (for display or reference) - * @param path The path to the config.ini file - * @param targetHash the target hash - * @param target The target. Can be null, if the target was not resolved. - * @param abiType Name of the abi. - * @param properties The property map. Can be null. - * @param status The {@link AvdStatus} of this AVD. Cannot be null. - */ - public AvdInfo(String name, String path, String targetHash, IAndroidTarget target, - String abiType, Map<String, String> properties, AvdStatus status) { - mName = name; - mPath = path; - mTargetHash = targetHash; - mTarget = target; - mAbiType = abiType; - mProperties = properties == null ? null : Collections.unmodifiableMap(properties); - mStatus = status; - } - - /** Returns the name of the AVD. */ - public String getName() { - return mName; - } - - /** Returns the path of the AVD data directory. */ - public String getPath() { - return mPath; - } - - /** Returns the processor type of the AVD. */ - public String getAbiType() { - return mAbiType; - } - - /** Convenience function to return a more user friendly name of the abi type. */ - public static String getPrettyAbiType(String raw) { - String s = null; - if (raw.equalsIgnoreCase(SdkConstants.ABI_ARMEABI)) { - s = "ARM (" + SdkConstants.ABI_ARMEABI + ")"; - } - else if (raw.equalsIgnoreCase(SdkConstants.ABI_INTEL_ATOM)) { - s = "Intel Atom (" + SdkConstants.ABI_INTEL_ATOM + ")"; - } - else { - s = raw + " (" + raw + ")"; - } - return s; - } - - /** - * Returns the emulator executable path - * @param sdkPath path of the sdk - * @return path of the emulator executable - */ - public String getEmulatorPath(String sdkPath) { - String path = sdkPath + SdkConstants.OS_SDK_TOOLS_FOLDER; - - // Start with base name of the emulator - path = path + SdkConstants.FN_EMULATOR; - - // If not using ARM, add processor type to emulator command line - boolean useAbi = !getAbiType().equalsIgnoreCase(SdkConstants.ABI_ARMEABI); - - if (useAbi) { - path = path + "-" + getAbiType(); - } - // Add OS appropriate emulator extension (e.g., .exe on windows) - path = path + SdkConstants.FN_EMULATOR_EXTENSION; - - // HACK: The AVD manager should look for "emulator" or for "emulator-abi" (if not arm). - // However this is a transition period and we don't have that unified "emulator" binary - // in AOSP so if we can't find the generic one, look for an abi-specific one with the - // special case that the armeabi one is actually named emulator-arm. - // TODO remove this kludge once no longer necessary. - if (!useAbi && !(new File(path).isFile())) { - path = sdkPath + SdkConstants.OS_SDK_TOOLS_FOLDER; - path = path + SdkConstants.FN_EMULATOR; - path = path + "-" + getAbiType().replace(SdkConstants.ABI_ARMEABI, "arm"); //$NON-NLS-1$ - path = path + SdkConstants.FN_EMULATOR_EXTENSION; - } - - return path; - } - - /** - * Returns the target hash string. - */ - public String getTargetHash() { - return mTargetHash; - } - - /** Returns the target of the AVD, or <code>null</code> if it has not been resolved. */ - public IAndroidTarget getTarget() { - return mTarget; - } - - /** Returns the {@link AvdStatus} of the receiver. */ - public AvdStatus getStatus() { - return mStatus; - } - - /** - * Helper method that returns the default AVD folder that would be used for a given - * AVD name <em>if and only if</em> the AVD was created with the default choice. - * <p/> - * Callers must NOT use this to "guess" the actual folder from an actual AVD since - * the purpose of the AVD .ini file is to be able to change this folder. - * <p/> - * For an actual existing AVD, callers must use {@link #getPath()} instead. - * - * @throws AndroidLocationException if there's a problem getting android root directory. - */ - public static File getAvdFolder(String name) throws AndroidLocationException { - return new File(AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD, - name + AvdManager.AVD_FOLDER_EXTENSION); - } - - /** - * Helper method that returns the .ini {@link File} for a given AVD name. - * @throws AndroidLocationException if there's a problem getting android root directory. - */ - public static File getIniFile(String name) throws AndroidLocationException { - String avdRoot = getBaseAvdFolder(); - return new File(avdRoot, name + INI_EXTENSION); - } - - /** - * Returns the .ini {@link File} for this AVD. - * @throws AndroidLocationException if there's a problem getting android root directory. - */ - public File getIniFile() throws AndroidLocationException { - return getIniFile(mName); - } - - /** - * Helper method that returns the Config {@link File} for a given AVD name. - */ - public static File getConfigFile(String path) { - return new File(path, CONFIG_INI); - } - - /** - * Returns the Config {@link File} for this AVD. - */ - public File getConfigFile() { - return getConfigFile(mPath); - } - - /** - * Returns an unmodifiable map of properties for the AVD. This can be null. - */ - public Map<String, String> getProperties() { - return mProperties; - } - - /** - * Returns the error message for the AVD or <code>null</code> if {@link #getStatus()} - * returns {@link AvdStatus#OK} - */ - public String getErrorMessage() { - try { - switch (mStatus) { - case ERROR_PATH: - return String.format("Missing AVD 'path' property in %1$s", getIniFile()); - case ERROR_CONFIG: - return String.format("Missing config.ini file in %1$s", mPath); - case ERROR_TARGET_HASH: - return String.format("Missing 'target' property in %1$s", getIniFile()); - case ERROR_TARGET: - return String.format("Unknown target '%1$s' in %2$s", - mTargetHash, getIniFile()); - case ERROR_PROPERTIES: - return String.format("Failed to parse properties from %1$s", - getConfigFile()); - case ERROR_IMAGE_DIR: - return String.format( - "Invalid value in image.sysdir. Run 'android update avd -n %1$s'", - mName); - case OK: - assert false; - return null; - } - } catch (AndroidLocationException e) { - return "Unable to get HOME folder."; - } - - return null; - } - - /** - * Returns whether an emulator is currently running the AVD. - */ - public boolean isRunning() { - File f = new File(mPath, "userdata-qemu.img.lock"); - return f.isFile(); - } - - /** - * Compares this object with the specified object for order. Returns a - * negative integer, zero, or a positive integer as this object is less - * than, equal to, or greater than the specified object. - * - * @param o the Object to be compared. - * @return a negative integer, zero, or a positive integer as this object is - * less than, equal to, or greater than the specified object. - */ - public int compareTo(AvdInfo o) { - // first handle possible missing targets (if the AVD failed to load for - // unresolved targets. - if (mTarget == null) { - return +1; - } else if (o.mTarget == null) { - return -1; - } - - // then compare the targets - int targetDiff = mTarget.compareTo(o.mTarget); - - if (targetDiff == 0) { - // same target? compare on the avd name - return mName.compareTo(o.mName); - } - - return targetDiff; - } - } - private final ArrayList<AvdInfo> mAllAvdList = new ArrayList<AvdInfo>(); private AvdInfo[] mValidAvdList; private AvdInfo[] mBrokenAvdList; private final SdkManager mSdkManager; /** - * Returns the base folder where AVDs are created. - * - * @throws AndroidLocationException - */ - public static String getBaseAvdFolder() throws AndroidLocationException { - return AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD; - } - - /** * Creates an AVD Manager for a given SDK represented by a {@link SdkManager}. * @param sdkManager The SDK. * @param log The log object to receive the log of the initial loading of the AVDs. @@ -506,6 +212,16 @@ public final class AvdManager { } /** + * Returns the base folder where AVDs are created. + * + * @throws AndroidLocationException + */ + public String getBaseAvdFolder() throws AndroidLocationException { + assert AndroidLocation.getFolder().endsWith(File.separator); + return AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD; + } + + /** * Returns the {@link SdkManager} associated with the {@link AvdManager}. */ public SdkManager getSdkManager() { @@ -689,12 +405,12 @@ public final class AvdManager { // Are some existing files/folders in the way of creating this AVD? try { - File file = AvdInfo.getIniFile(name); + File file = AvdInfo.getDefaultIniFile(this, name); if (file.exists()) { return Pair.of(AvdConflict.CONFLICT_EXISTING_PATH, file.getPath()); } - file = AvdInfo.getAvdFolder(name); + file = AvdInfo.getDefaultAvdFolder(this, name); if (file.exists()) { return Pair.of(AvdConflict.CONFLICT_EXISTING_PATH, file.getPath()); } @@ -730,7 +446,9 @@ public final class AvdManager { * Creates a new AVD. It is expected that there is no existing AVD with this name already. * * @param avdFolder the data folder for the AVD. It will be created as needed. - * @param name the name of the AVD + * Unless you want to locate it in a specific directory, the ideal default is + * {@code AvdManager.AvdInfo.getAvdFolder}. + * @param avdName the name of the AVD * @param target the target of the AVD * @param abiType the abi type of the AVD * @param skinName the name of the skin. Can be null. Must have been verified by caller. @@ -747,7 +465,7 @@ public final class AvdManager { */ public AvdInfo createAvd( File avdFolder, - String name, + String avdName, IAndroidTarget target, String abiType, String skinName, @@ -789,7 +507,7 @@ public final class AvdManager { } // actually write the ini file - iniFile = createAvdIniFile(name, avdFolder, target, removePrevious); + iniFile = createAvdIniFile(avdName, avdFolder, target, removePrevious); // writes the userdata.img in it. String imagePath = target.getImagePath(abiType); @@ -1004,17 +722,17 @@ public final class AvdManager { if (target.isPlatform()) { if (editExisting) { report.append(String.format("Updated AVD '%1$s' based on %2$s", - name, target.getName())); + avdName, target.getName())); } else { report.append(String.format("Created AVD '%1$s' based on %2$s", - name, target.getName())); + avdName, target.getName())); } } else { if (editExisting) { - report.append(String.format("Updated AVD '%1$s' based on %2$s (%3$s)", name, + report.append(String.format("Updated AVD '%1$s' based on %2$s (%3$s)", avdName, target.getName(), target.getVendor())); } else { - report.append(String.format("Created AVD '%1$s' based on %2$s (%3$s)", name, + report.append(String.format("Created AVD '%1$s' based on %2$s (%3$s)", avdName, target.getName(), target.getVendor())); } } @@ -1033,12 +751,14 @@ public final class AvdManager { log.printf(report.toString()); // create the AvdInfo object, and add it to the list - AvdInfo newAvdInfo = new AvdInfo(name, + AvdInfo newAvdInfo = new AvdInfo( + avdName, + iniFile, avdFolder.getAbsolutePath(), target.hashString(), target, abiType, values); - AvdInfo oldAvdInfo = getAvd(name, false /*validAvdOnly*/); + AvdInfo oldAvdInfo = getAvd(avdName, false /*validAvdOnly*/); synchronized (mAllAvdList) { if (oldAvdInfo != null && (removePrevious || editExisting)) { @@ -1051,10 +771,11 @@ public final class AvdManager { if ((removePrevious || editExisting) && newAvdInfo != null && oldAvdInfo != null && - !oldAvdInfo.getPath().equals(newAvdInfo.getPath())) { - log.warning("Removing previous AVD directory at %s", oldAvdInfo.getPath()); + !oldAvdInfo.getDataFolderPath().equals(newAvdInfo.getDataFolderPath())) { + log.warning("Removing previous AVD directory at %s", + oldAvdInfo.getDataFolderPath()); // Remove the old data directory - File dir = new File(oldAvdInfo.getPath()); + File dir = new File(oldAvdInfo.getDataFolderPath()); try { deleteContentOf(dir); dir.delete(); @@ -1222,7 +943,7 @@ public final class AvdManager { IAndroidTarget target, boolean removePrevious) throws AndroidLocationException, IOException { - File iniFile = AvdInfo.getIniFile(name); + File iniFile = AvdInfo.getDefaultIniFile(this, name); if (removePrevious) { if (iniFile.isFile()) { @@ -1250,7 +971,7 @@ public final class AvdManager { */ private File createAvdIniFile(AvdInfo info) throws AndroidLocationException, IOException { return createAvdIniFile(info.getName(), - new File(info.getPath()), + new File(info.getDataFolderPath()), info.getTarget(), false /*removePrevious*/); } @@ -1283,7 +1004,7 @@ public final class AvdManager { } } - String path = avdInfo.getPath(); + String path = avdInfo.getDataFolderPath(); if (path != null) { f = new File(path); if (f.exists()) { @@ -1305,8 +1026,6 @@ public final class AvdManager { return true; } - } catch (AndroidLocationException e) { - log.error(e, null); } catch (IOException e) { log.error(e, null); } catch (SecurityException e) { @@ -1333,17 +1052,25 @@ public final class AvdManager { try { if (paramFolderPath != null) { - File f = new File(avdInfo.getPath()); - log.warning("Moving '%1$s' to '%2$s'.", avdInfo.getPath(), paramFolderPath); + File f = new File(avdInfo.getDataFolderPath()); + log.warning("Moving '%1$s' to '%2$s'.", + avdInfo.getDataFolderPath(), + paramFolderPath); if (!f.renameTo(new File(paramFolderPath))) { log.error(null, "Failed to move '%1$s' to '%2$s'.", - avdInfo.getPath(), paramFolderPath); + avdInfo.getDataFolderPath(), paramFolderPath); return false; } // update AVD info - AvdInfo info = new AvdInfo(avdInfo.getName(), paramFolderPath, - avdInfo.getTargetHash(), avdInfo.getTarget(), avdInfo.getAbiType(), avdInfo.getProperties()); + AvdInfo info = new AvdInfo( + avdInfo.getName(), + avdInfo.getIniFile(), + paramFolderPath, + avdInfo.getTargetHash(), + avdInfo.getTarget(), + avdInfo.getAbiType(), + avdInfo.getProperties()); replaceAvd(avdInfo, info); // update the ini file @@ -1352,7 +1079,7 @@ public final class AvdManager { if (newName != null) { File oldIniFile = avdInfo.getIniFile(); - File newIniFile = AvdInfo.getIniFile(newName); + File newIniFile = AvdInfo.getDefaultIniFile(this, newName); log.warning("Moving '%1$s' to '%2$s'.", oldIniFile.getPath(), newIniFile.getPath()); if (!oldIniFile.renameTo(newIniFile)) { @@ -1362,9 +1089,14 @@ public final class AvdManager { } // update AVD info - AvdInfo info = new AvdInfo(newName, avdInfo.getPath(), - avdInfo.getTargetHash(), avdInfo.getTarget(), - avdInfo.getAbiType(), avdInfo.getProperties()); + AvdInfo info = new AvdInfo( + newName, + avdInfo.getIniFile(), + avdInfo.getDataFolderPath(), + avdInfo.getTargetHash(), + avdInfo.getTarget(), + avdInfo.getAbiType(), + avdInfo.getProperties()); replaceAvd(avdInfo, info); } @@ -1409,19 +1141,20 @@ public final class AvdManager { * <p/> * This lists the $HOME/.android/avd/<name>.ini files. * Such files are properties file than then indicate where the AVD folder is located. + * <p/> + * Note: the method is to be considered private. It is made protected so that + * unit tests can easily override the AVD root. * * @return A new {@link File} array or null. The array might be empty. * @throws AndroidLocationException if there's a problem getting android root directory. */ private File[] buildAvdFilesList() throws AndroidLocationException { - // get the Android prefs location. - String avdRoot = AvdManager.getBaseAvdFolder(); + File folder = new File(getBaseAvdFolder()); // ensure folder validity. - File folder = new File(avdRoot); if (folder.isFile()) { throw new AndroidLocationException( - String.format("%1$s is not a valid folder.", avdRoot)); + String.format("%1$s is not a valid folder.", folder.getAbsolutePath())); } else if (folder.exists() == false) { // folder is not there, we create it and return folder.mkdirs(); @@ -1466,14 +1199,14 @@ public final class AvdManager { /** * Parses an AVD .ini file to create an {@link AvdInfo}. * - * @param path The path to the AVD .ini file + * @param iniPath The path to the AVD .ini file * @param log the log object to receive action logs. Cannot be null. * @return A new {@link AvdInfo} with an {@link AvdStatus} indicating whether this AVD is * valid or not. */ - private AvdInfo parseAvdInfo(File path, ISdkLog log) { + private AvdInfo parseAvdInfo(File iniPath, ISdkLog log) { Map<String, String> map = ProjectProperties.parsePropertyFile( - new FileWrapper(path), + new FileWrapper(iniPath), log); String avdPath = map.get(AVD_INFO_PATH); @@ -1501,8 +1234,8 @@ public final class AvdManager { } // get name - String name = path.getName(); - Matcher matcher = INI_NAME_PATTERN.matcher(path.getName()); + String name = iniPath.getName(); + Matcher matcher = INI_NAME_PATTERN.matcher(iniPath.getName()); if (matcher.matches()) { name = matcher.group(1); } @@ -1557,6 +1290,7 @@ public final class AvdManager { AvdInfo info = new AvdInfo( name, + iniPath, avdPath, targetHash, target, @@ -1792,7 +1526,7 @@ public final class AvdManager { } // now write the config file - File configIniFile = new File(avd.getPath(), CONFIG_INI); + File configIniFile = new File(avd.getDataFolderPath(), CONFIG_INI); writeIniFile(configIniFile, properties); // finally create a new AvdInfo for this unbroken avd and add it to the list. @@ -1801,7 +1535,8 @@ public final class AvdManager { // FIXME: We may want to create this AvdInfo by reparsing the AVD instead. This could detect other errors. AvdInfo newAvd = new AvdInfo( avd.getName(), - avd.getPath(), + avd.getIniFile(), + avd.getDataFolderPath(), avd.getTargetHash(), avd.getTarget(), avd.getAbiType(), diff --git a/sdkmanager/libs/sdkuilib/.classpath b/sdkmanager/libs/sdkuilib/.classpath index 7f1dec3..9080f57 100644 --- a/sdkmanager/libs/sdkuilib/.classpath +++ b/sdkmanager/libs/sdkuilib/.classpath @@ -8,6 +8,6 @@ <classpathentry combineaccessrules="false" kind="src" path="/AndroidPrefs"/> <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/> <classpathentry combineaccessrules="false" kind="src" path="/common"/> - <classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/swtmenubar.jar"/> + <classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/swtmenubar.jar" sourcepath="/ANDROID_SRC/sdk/swtmenubar/src"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/sdkmanager/libs/sdkuilib/README b/sdkmanager/libs/sdkuilib/README index 3d68520..dee4a24 100644 --- a/sdkmanager/libs/sdkuilib/README +++ b/sdkmanager/libs/sdkuilib/README @@ -13,7 +13,7 @@ In order to compile the project: - Create a new user library named ANDROID_SWT - Add the following 4 JAR files: - - prebuild/<platform>/swt/swt.jar + - prebuilt/<platform>/swt/swt.jar - prebuilt/common/eclipse/org.eclipse.core.commands_3.*.jar - prebuilt/common/eclipse/org.eclipse.equinox.common_3.*.jar - prebuilt/common/eclipse/org.eclipse.jface_3.*.jar @@ -30,10 +30,16 @@ Define a classpath variable in Eclipse: - Open Preferences > Java > Build Path > Classpath Variables - Create a new classpath variable named ANDROID_OUT_FRAMEWORK - Set its folder value to <Android tree>/out/host/<platform>/framework +- Create a new classpath variable named ANDROID_SRC +- Set its folder value to <Android tree> You might need to clean the SdkUiLib project (Project > Clean...) after you add the new classpath variable, otherwise previous errors might not go away automatically. +The ANDROID_SRC part should be optional. It allows you to have access to +the SwtMenuBar generic parts from the Java editor. + + -- EOF diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java index 7594f16..7c78983 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java @@ -17,7 +17,6 @@ package com.android.sdkuilib.internal.repository;
import com.android.prefs.AndroidLocation.AndroidLocationException;
-import com.android.sdklib.internal.avd.AvdManager;
import com.android.sdkuilib.internal.widgets.AvdSelector;
import com.android.sdkuilib.internal.widgets.AvdSelector.DisplayMode;
import com.android.sdkuilib.repository.ISdkChangeListener;
@@ -58,7 +57,7 @@ public class AvdManagerPage extends Composite implements ISdkChangeListener { try {
label.setText(String.format(
"List of existing Android Virtual Devices located at %s",
- AvdManager.getBaseAvdFolder()));
+ mUpdaterData.getAvdManager().getBaseAvdFolder()));
} catch (AndroidLocationException e) {
label.setText(e.getMessage());
}
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/PackagesPage.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/PackagesPage.java index 749f927..708c2d6 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/PackagesPage.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/PackagesPage.java @@ -56,9 +56,10 @@ import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; @@ -74,6 +75,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Map.Entry; /** * Page that displays both locally installed packages as well as all known @@ -86,16 +88,46 @@ public class PackagesPage extends Composite private static final String ICON_CAT_OTHER = "pkgcat_other_16.png"; //$NON-NLS-1$ private static final String ICON_CAT_PLATFORM = "pkgcat_16.png"; //$NON-NLS-1$ private static final String ICON_SORT_BY_SOURCE = "source_icon16.png"; //$NON-NLS-1$ - private static final String ICON_COLUMN_NAME = "platform_pkg_16.png"; //$NON-NLS-1$ + private static final String ICON_SORT_BY_API = "platform_pkg_16.png"; //$NON-NLS-1$ private static final String ICON_PKG_NEW = "pkg_new_16.png"; //$NON-NLS-1$ private static final String ICON_PKG_UPDATE = "pkg_update_16.png"; //$NON-NLS-1$ private static final String ICON_PKG_INSTALLED = "pkg_installed_16.png"; //$NON-NLS-1$ + enum MenuAction { + RELOAD (SWT.NONE, "Reload"), + SHOW_ADDON_SITES (SWT.NONE, "Manage Sources..."), + TOGGLE_SHOW_ARCHIVES (SWT.CHECK, "Show Archives"), + TOGGLE_SHOW_INSTALLED_PKG (SWT.CHECK, "Show Installed Packages"), + TOGGLE_SHOW_OBSOLETE_PKG (SWT.CHECK, "Show Obsolete Packages"), + TOGGLE_SHOW_UPDATE_NEW_PKG (SWT.CHECK, "Show Updates/New Packages"), + SORT_API_LEVEL (SWT.RADIO, "Sort by API Level"), + SORT_SOURCE (SWT.RADIO, "Sort by Source") + ; + + private final int mMenuStyle; + private final String mMenuTitle; + + MenuAction(int menuStyle, String menuTitle) { + mMenuStyle = menuStyle; + mMenuTitle = menuTitle; + } + + public int getMenuStyle() { + return mMenuStyle; + } + + public String getMenuTitle() { + return mMenuTitle; + } + }; + + private final Map<MenuAction, MenuItem> mMenuActions = new HashMap<MenuAction, MenuItem>(); + private final List<PkgItem> mPackages = new ArrayList<PkgItem>(); private final List<PkgCategory> mCategories = new ArrayList<PkgCategory>(); private final UpdaterData mUpdaterData; - private boolean mDisplayArchives = false; // TODO: toggle via a menu item + private boolean mDisplayArchives = false; private Text mTextSdkOsPath; private Button mCheckSortSource; @@ -116,7 +148,7 @@ public class PackagesPage extends Composite private TreeViewerColumn mColumnStatus; private Color mColorUpdate; private Font mTreeFontItalic; - private Button mButtonReload; + private TreeColumn mTreeColumnName; public PackagesPage(Composite parent, UpdaterData updaterData) { super(parent, SWT.NONE); @@ -134,7 +166,7 @@ public class PackagesPage extends Composite } } - protected void createContents(Composite parent) { + private void createContents(Composite parent) { GridLayout gridLayout = new GridLayout(2, false); gridLayout.marginWidth = 0; gridLayout.marginHeight = 0; @@ -169,11 +201,12 @@ public class PackagesPage extends Composite mTree.setHeaderVisible(true); mTree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); + // column name icon is set in sortPackages() depending on the current filter type + // (e.g. API level or source) mColumnName = new TreeViewerColumn(mTreeViewer, SWT.NONE); - TreeColumn treeColumn1 = mColumnName.getColumn(); - treeColumn1.setImage(getImage(ICON_COLUMN_NAME)); - treeColumn1.setWidth(340); - treeColumn1.setText("Name"); + mTreeColumnName = mColumnName.getColumn(); + mTreeColumnName.setWidth(340); + mTreeColumnName.setText("Name"); mColumnApi = new TreeViewerColumn(mTreeViewer, SWT.NONE); TreeColumn treeColumn2 = mColumnApi.getColumn(); @@ -197,7 +230,7 @@ public class PackagesPage extends Composite mGroupOptions = new Composite(mGroupPackages, SWT.NONE); mGroupOptions.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); - GridLayout gl_GroupOptions = new GridLayout(7, false); + GridLayout gl_GroupOptions = new GridLayout(6, false); gl_GroupOptions.marginWidth = 0; gl_GroupOptions.marginHeight = 0; mGroupOptions.setLayout(gl_GroupOptions); @@ -213,7 +246,6 @@ public class PackagesPage extends Composite sortPackages(true /*updateButtons*/); } }); - mCheckFilterNew.setImage(getImage(ICON_PKG_NEW)); mCheckFilterNew.setSelection(true); mCheckFilterNew.setText("Updates/New"); @@ -225,7 +257,6 @@ public class PackagesPage extends Composite sortPackages(true /*updateButtons*/); } }); - mCheckFilterInstalled.setImage(getImage(ICON_PKG_INSTALLED)); mCheckFilterInstalled.setSelection(true); mCheckFilterInstalled.setText("Installed"); @@ -240,17 +271,6 @@ public class PackagesPage extends Composite mCheckFilterObsolete.setSelection(false); mCheckFilterObsolete.setText("Obsolete"); - mButtonReload = new Button(mGroupOptions, SWT.NONE); - mButtonReload.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); - mButtonReload.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - onButtonReload(); - } - }); - mButtonReload.setToolTipText("Reload the package list"); - mButtonReload.setText("Reload"); - Label placeholder2 = new Label(mGroupOptions, SWT.NONE); placeholder2.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); @@ -278,7 +298,6 @@ public class PackagesPage extends Composite expandInitial(mCategories); } }); - mCheckSortApi.setImage(getImage(ICON_COLUMN_NAME)); mCheckSortApi.setText("API level"); mCheckSortApi.setSelection(true); @@ -292,19 +311,8 @@ public class PackagesPage extends Composite expandInitial(mCategories); } }); - mCheckSortSource.setImage(getImage(ICON_SORT_BY_SOURCE)); mCheckSortSource.setText("Source"); - Link link = new Link(mGroupOptions, SWT.NONE); - link.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - onButtonAddonSites(); - } - }); - link.setToolTipText("Manage the list of add-on sites"); - link.setText("<a>Manage Sources</a>"); - new Label(mGroupOptions, SWT.NONE); new Label(mGroupOptions, SWT.NONE); @@ -335,6 +343,124 @@ public class PackagesPage extends Composite // Hide everything down-below from SWT designer //$hide>>$ + + // --- menu interactions --- + + public void registerMenuAction(final MenuAction action, MenuItem item) { + item.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + Button button = null; + + switch (action) { + case RELOAD: + loadPackages(); + break; + case SHOW_ADDON_SITES: + AddonSitesDialog d = new AddonSitesDialog(getShell(), mUpdaterData); + if (d.open()) { + loadPackages(); + } + break; + case TOGGLE_SHOW_ARCHIVES: + mDisplayArchives = !mDisplayArchives; + sortPackages(true /*updateButtons*/); + break; + case TOGGLE_SHOW_INSTALLED_PKG: + button = mCheckFilterInstalled; + break; + case TOGGLE_SHOW_OBSOLETE_PKG: + button = mCheckFilterObsolete; + break; + case TOGGLE_SHOW_UPDATE_NEW_PKG: + button = mCheckFilterNew; + break; + case SORT_API_LEVEL: + button = mCheckSortApi; + break; + case SORT_SOURCE: + button = mCheckSortSource; + break; + } + + if (button != null && !button.isDisposed()) { + // Toggle this button (radio or checkbox) + + boolean value = button.getSelection(); + + // SWT doesn't automatically switch radio buttons when using the + // Widget#setSelection method, so we'll do it here manually. + if (!value && (button.getStyle() & SWT.RADIO) != 0) { + // we'll be selecting this radio button, so deselect all ther other ones + // in the parent group. + for (Control child : button.getParent().getChildren()) { + if (child instanceof Button && + child != button && + (child.getStyle() & SWT.RADIO) != 0) { + ((Button) child).setSelection(value); + } + } + } + + button.setSelection(!value); + + // SWT doesn't actually invoke the listeners when using Widget#setSelection + // so let's run the actual action. + button.notifyListeners(SWT.Selection, new Event()); + } + + updateMenuCheckmarks(); + } + }); + + mMenuActions.put(action, item); + } + + // --- internal methods --- + + private void updateMenuCheckmarks() { + + for (Entry<MenuAction, MenuItem> entry : mMenuActions.entrySet()) { + MenuAction action = entry.getKey(); + MenuItem item = entry.getValue(); + + if (action.getMenuStyle() == SWT.NONE) { + continue; + } + + boolean value = false; + Button button = null; + + switch (action) { + case TOGGLE_SHOW_ARCHIVES: + value = mDisplayArchives; + break; + case TOGGLE_SHOW_INSTALLED_PKG: + button = mCheckFilterInstalled; + break; + case TOGGLE_SHOW_OBSOLETE_PKG: + button = mCheckFilterObsolete; + break; + case TOGGLE_SHOW_UPDATE_NEW_PKG: + button = mCheckFilterNew; + break; + case SORT_API_LEVEL: + button = mCheckSortApi; + break; + case SORT_SOURCE: + button = mCheckSortSource; + break; + } + + if (button != null && !button.isDisposed()) { + value = button.getSelection(); + } + + item.setSelection(value); + } + + } + private void postCreate() { if (mUpdaterData != null) { mTextSdkOsPath.setText(mUpdaterData.getOsSdkRoot()); @@ -442,6 +568,7 @@ public class PackagesPage extends Composite } finally { enableUi(mGroupPackages, true); updateButtonsState(); + updateMenuCheckmarks(); } } @@ -458,12 +585,13 @@ public class PackagesPage extends Composite private void sortPackages(boolean updateButtons) { if (mCheckSortApi != null && !mCheckSortApi.isDisposed() && mCheckSortApi.getSelection()) { - sortByAPI(); + sortByApiLevel(); } else { sortBySource(); } if (updateButtons) { updateButtonsState(); + updateMenuCheckmarks(); } } @@ -472,10 +600,14 @@ public class PackagesPage extends Composite * This does an update in-place of the mCategories list so that the table * can preserve its state (checked / expanded / selected) properly. */ - private void sortByAPI() { + private void sortByApiLevel() { ImageFactory imgFactory = mUpdaterData.getImageFactory(); + if (!mTreeColumnName.isDisposed()) { + mTreeColumnName.setImage(getImage(ICON_SORT_BY_API)); + } + // keep a map of the initial state so that we can detect which items or categories are // no longer being used, so that we can removed them at the end of the in-place update. final Map<Integer, Pair<PkgCategory, HashSet<PkgItem>> > unusedItemsMap = @@ -612,6 +744,11 @@ public class PackagesPage extends Composite * Recompute the tree by sorting all packages by source. */ private void sortBySource() { + + if (!mTreeColumnName.isDisposed()) { + mTreeColumnName.setImage(getImage(ICON_SORT_BY_SOURCE)); + } + mCategories.clear(); Set<SdkSource> sourceSet = new HashSet<SdkSource>(); @@ -816,18 +953,7 @@ public class PackagesPage extends Composite mButtonDelete.setEnabled(canDelete); } - protected void onButtonReload() { - loadPackages(); - } - - protected void onButtonAddonSites() { - AddonSitesDialog d = new AddonSitesDialog(getShell(), mUpdaterData); - if (d.open()) { - loadPackages(); - } - } - - protected void onButtonInstall() { + private void onButtonInstall() { ArrayList<Archive> archives = new ArrayList<Archive>(); if (mDisplayArchives) { @@ -883,7 +1009,7 @@ public class PackagesPage extends Composite } } - protected void onButtonDelete() { + private void onButtonDelete() { // Find selected local packages to be delete Object[] checked = mTreeViewer.getCheckedElements(); if (checked == null) { diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl2.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl2.java index dfdf886..6b3cd66 100755 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl2.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/UpdaterWindowImpl2.java @@ -21,6 +21,7 @@ import com.android.menubar.IMenuBarCallback; import com.android.menubar.MenuBarEnhancer;
import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants;
+import com.android.sdkuilib.internal.repository.PackagesPage.MenuAction;
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
import com.android.sdkuilib.internal.tasks.ProgressView;
import com.android.sdkuilib.internal.tasks.ProgressViewFactory;
@@ -100,9 +101,9 @@ public class UpdaterWindowImpl2 implements IUpdaterWindow { }
createShell();
- createMenuBar();
preCreateContent();
createContents();
+ createMenuBar();
mShell.open();
mShell.layout();
@@ -187,30 +188,58 @@ public class UpdaterWindowImpl2 implements IUpdaterWindow { Menu menuPkgs = new Menu(menuBarPackages);
menuBarPackages.setMenu(menuPkgs);
- MenuItem ShowUpdatesnew = new MenuItem(menuPkgs, SWT.NONE);
- ShowUpdatesnew.setText("Show Updates/New Packages");
-
- MenuItem ShowInstalled = new MenuItem(menuPkgs, SWT.NONE);
- ShowInstalled.setText("Show Installed Packages");
-
- MenuItem ShowObsoletePackages = new MenuItem(menuPkgs, SWT.NONE);
- ShowObsoletePackages.setText("Show Obsolete Packages");
-
- MenuItem ShowArchives = new MenuItem(menuPkgs, SWT.NONE);
- ShowArchives.setText("Show Archives");
+ MenuItem showUpdatesNew = new MenuItem(menuPkgs,
+ MenuAction.TOGGLE_SHOW_UPDATE_NEW_PKG.getMenuStyle());
+ showUpdatesNew.setText(
+ MenuAction.TOGGLE_SHOW_UPDATE_NEW_PKG.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.TOGGLE_SHOW_UPDATE_NEW_PKG, showUpdatesNew);
+
+ MenuItem showInstalled = new MenuItem(menuPkgs,
+ MenuAction.TOGGLE_SHOW_INSTALLED_PKG.getMenuStyle());
+ showInstalled.setText(
+ MenuAction.TOGGLE_SHOW_INSTALLED_PKG.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.TOGGLE_SHOW_INSTALLED_PKG, showInstalled);
+
+ MenuItem showObsoletePackages = new MenuItem(menuPkgs,
+ MenuAction.TOGGLE_SHOW_OBSOLETE_PKG.getMenuStyle());
+ showObsoletePackages.setText(
+ MenuAction.TOGGLE_SHOW_OBSOLETE_PKG.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.TOGGLE_SHOW_OBSOLETE_PKG, showObsoletePackages);
+
+ MenuItem showArchives = new MenuItem(menuPkgs,
+ MenuAction.TOGGLE_SHOW_ARCHIVES.getMenuStyle());
+ showArchives.setText(
+ MenuAction.TOGGLE_SHOW_ARCHIVES.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.TOGGLE_SHOW_ARCHIVES, showArchives);
new MenuItem(menuPkgs, SWT.SEPARATOR);
- MenuItem sortByApi = new MenuItem(menuPkgs, SWT.NONE);
- sortByApi.setText("Sort by API Level");
+ MenuItem sortByApi = new MenuItem(menuPkgs,
+ MenuAction.SORT_API_LEVEL.getMenuStyle());
+ sortByApi.setText(
+ MenuAction.SORT_API_LEVEL.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.SORT_API_LEVEL, sortByApi);
- MenuItem sortBySource = new MenuItem(menuPkgs, SWT.NONE);
- sortBySource.setText("Sort by Source");
+ MenuItem sortBySource = new MenuItem(menuPkgs,
+ MenuAction.SORT_SOURCE.getMenuStyle());
+ sortBySource.setText(
+ MenuAction.SORT_SOURCE.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.SORT_SOURCE, sortBySource);
new MenuItem(menuPkgs, SWT.SEPARATOR);
- MenuItem reload = new MenuItem(menuPkgs, SWT.NONE);
- reload.setText("Reload");
+ MenuItem reload = new MenuItem(menuPkgs,
+ MenuAction.RELOAD.getMenuStyle());
+ reload.setText(
+ MenuAction.RELOAD.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.RELOAD, reload);
MenuItem menuBarTools = new MenuItem(menuBar, SWT.CASCADE);
menuBarTools.setText("Tools");
@@ -221,8 +250,12 @@ public class UpdaterWindowImpl2 implements IUpdaterWindow { MenuItem manageAvds = new MenuItem(menuTools, SWT.NONE);
manageAvds.setText("Manage AVDs...");
- MenuItem manageSources = new MenuItem(menuTools, SWT.NONE);
- manageSources.setText("Manage Sources...");
+ MenuItem manageSources = new MenuItem(menuTools,
+ MenuAction.SHOW_ADDON_SITES.getMenuStyle());
+ manageSources.setText(
+ MenuAction.SHOW_ADDON_SITES.getMenuTitle());
+ mPkgPage.registerMenuAction(
+ MenuAction.SHOW_ADDON_SITES, manageSources);
MenuBarEnhancer.setupMenu(APP_NAME, menuTools, new IMenuBarCallback() {
public void onPreferencesMenuSelected() {
@@ -237,7 +270,9 @@ public class UpdaterWindowImpl2 implements IUpdaterWindow { public void printError(String format, Object... args) {
if (mUpdaterData != null) {
- mUpdaterData.getSdkLog().warning(format, args);
+ // TODO: right now dump to stderr. Use sdklog later.
+ //mUpdaterData.getSdkLog().error(null, format, args);
+ System.err.printf(format, args);
}
}
});
diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java index b545dbd..0932378 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdCreationDialog.java @@ -22,10 +22,10 @@ import com.android.sdklib.IAndroidTarget; import com.android.sdklib.ISdkLog; import com.android.sdklib.SdkConstants; import com.android.sdklib.SdkManager; +import com.android.sdklib.internal.avd.AvdInfo; import com.android.sdklib.internal.avd.AvdManager; import com.android.sdklib.internal.avd.HardwareProperties; import com.android.sdklib.internal.avd.AvdManager.AvdConflict; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdklib.internal.avd.HardwareProperties.HardwareProperty; import com.android.sdklib.internal.project.ProjectProperties; import com.android.sdkuilib.internal.repository.icons.ImageFactory; @@ -1312,7 +1312,7 @@ final class AvdCreationDialog extends GridDialog { File avdFolder = null; try { - avdFolder = AvdManager.AvdInfo.getAvdFolder(avdName); + avdFolder = AvdInfo.getDefaultAvdFolder(mAvdManager, avdName); } catch (AndroidLocationException e) { return false; } diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java index 46f3eaf..6a85c14 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdDetailsDialog.java @@ -18,9 +18,9 @@ package com.android.sdkuilib.internal.widgets; import com.android.sdklib.AndroidVersion; import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.internal.avd.AvdInfo; import com.android.sdklib.internal.avd.AvdManager; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo.AvdStatus; +import com.android.sdklib.internal.avd.AvdInfo.AvdStatus; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; @@ -103,7 +103,7 @@ final class AvdDetailsDialog extends Dialog { displayValue(c, "Name:", mAvdInfo.getName()); displayValue(c, "ABI:", AvdInfo.getPrettyAbiType(mAvdInfo.getAbiType())); - displayValue(c, "Path:", mAvdInfo.getPath()); + displayValue(c, "Path:", mAvdInfo.getDataFolderPath()); if (mAvdInfo.getStatus() != AvdStatus.OK) { displayValue(c, "Error:", mAvdInfo.getErrorMessage()); diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java index 86a094f..56f2c7e 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java @@ -20,9 +20,9 @@ import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.ISdkLog; import com.android.sdklib.NullSdkLog; +import com.android.sdklib.internal.avd.AvdInfo; import com.android.sdklib.internal.avd.AvdManager; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo.AvdStatus; +import com.android.sdklib.internal.avd.AvdInfo.AvdStatus; import com.android.sdklib.internal.repository.ITask; import com.android.sdklib.internal.repository.ITaskMonitor; import com.android.sdkuilib.internal.repository.SettingsController; diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java index 77f47d1..7731dc1 100644 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java +++ b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java @@ -16,8 +16,8 @@ package com.android.sdkuilib.internal.widgets; +import com.android.sdklib.internal.avd.AvdInfo; import com.android.sdklib.internal.avd.AvdManager; -import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdkuilib.internal.repository.SettingsController; import com.android.sdkuilib.ui.GridDialog; diff --git a/swtmenubar/.classpath b/swtmenubar/.classpath index 827ba88..0f69dc5 100644 --- a/swtmenubar/.classpath +++ b/swtmenubar/.classpath @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> - <classpathentry excluding="org/eclipse/ui/internal/cocoa/CocoaUIEnhancer.java" kind="src" path="src"/> + <classpathentry kind="src" path="src"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/> <classpathentry kind="output" path="bin"/> diff --git a/swtmenubar/Android.mk b/swtmenubar/Android.mk index 333684f..25e80da 100644 --- a/swtmenubar/Android.mk +++ b/swtmenubar/Android.mk @@ -27,7 +27,10 @@ endif LOCAL_MODULE := swtmenubar LOCAL_MODULE_TAGS := optional -LOCAL_JAVA_LIBRARIES := swt +LOCAL_JAVA_LIBRARIES := \ + swt \ + org.eclipse.jface_3.4.2.M20090107-0800 + include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/swtmenubar/README b/swtmenubar/README index cc9fa61..ba7c25a 100755 --- a/swtmenubar/README +++ b/swtmenubar/README @@ -57,9 +57,24 @@ Then add a variable to the Build Path of the target project: - Select swtmenubar.jar (which you previously built at step 1) -Remember that if you then edit the SwtMenuBar project in Eclipse -you will need to rebuild it using the command-line before the -changes are propagated in the target applications that uses it. +3- Tip for developing this library: + +Keep in mind that src-darwin folder must not be added to the +source folder list, otherwise the library would not compile +on Windows or Linux. + +If you change anything to IMenuBarCallback, make sure to test +on a Mac to be sure you're not breaking the API. + +To work on this on a Mac, you can either: +a- simply temporarily add src-darwin as a source folder to the + build path and remove it before submitting. +b- or directly edit the java files and rebuild the library using + 'make swtmenubar' from a shell. + +To test the library, use 'make swtmenubar'. This will build the +library in out/... and the sdkmanager project is already setup +to find it there. -- EOF diff --git a/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java b/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java index 328597a..45dacfb 100755 --- a/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java +++ b/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCarbon.java @@ -38,11 +38,14 @@ public final class MenuBarEnhancerCarbon implements IMenuBarEnhancer { public MenuBarEnhancerCarbon() { } + public MenuBarMode getMenuBarMode() { + return MenuBarMode.MAC_OS; + } + public void setupMenu( String appName, - Menu swtMenu, + Display display, final IMenuBarCallback callbacks) { - final Display display = swtMenu.getDisplay(); // Callback target Object target = new Object() { diff --git a/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCocoa.java b/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCocoa.java new file mode 100644 index 0000000..170603a --- /dev/null +++ b/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCocoa.java @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2011 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. + * + * History: + * Original code by the <a href="http://www.simidude.com/blog/2008/macify-a-swt-application-in-a-cross-platform-way/">CarbonUIEnhancer from Agynami</a> + * with the implementation being modified from the <a href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.ui.cocoa/src/org/eclipse/ui/internal/cocoa/CocoaUIEnhancer.java">org.eclipse.ui.internal.cocoa.CocoaUIEnhancer</a>, + * then modified by http://www.transparentech.com/opensource/cocoauienhancer to use reflection + * rather than 'link' to SWT cocoa, and finally modified to be usable by the SwtMenuBar project. + */ + +package com.android.menubar.internal; + +import com.android.menubar.IMenuBarCallback; +import com.android.menubar.IMenuBarEnhancer; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.internal.C; +import org.eclipse.swt.internal.Callback; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Menu; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class MenuBarEnhancerCocoa implements IMenuBarEnhancer { + + private static final long kAboutMenuItem = 0; + private static final long kPreferencesMenuItem = 2; + // private static final long kServicesMenuItem = 4; + // private static final long kHideApplicationMenuItem = 6; + private static final long kQuitMenuItem = 10; + + static long mSelPreferencesMenuItemSelected; + static long mSelAboutMenuItemSelected; + static Callback mProc3Args; + + private String mAppName; + + /** + * Class invoked via the Callback object to run the about and preferences + * actions. + * <p> + * If you don't use JFace in your application (SWT only), change the + * {@link org.eclipse.jface.action.IAction}s to + * {@link org.eclipse.swt.widgets.Listener}s. + * </p> + */ + private static class ActionProctarget { + private final IMenuBarCallback mCallbacks; + + public ActionProctarget(IMenuBarCallback callbacks) { + mCallbacks = callbacks; + } + + /** + * Will be called on 32bit SWT. + */ + @SuppressWarnings("unused") + public int actionProc(int id, int sel, int arg0) { + return (int) actionProc((long) id, (long) sel, (long) arg0); + } + + /** + * Will be called on 64bit SWT. + */ + public long actionProc(long id, long sel, long arg0) { + if (sel == mSelAboutMenuItemSelected) { + mCallbacks.onAboutMenuSelected(); + } else if (sel == mSelPreferencesMenuItemSelected) { + mCallbacks.onPreferencesMenuSelected(); + } else { + // Unknown selection! + } + // Return value is not used. + return 0; + } + } + + /** + * Construct a new CocoaUIEnhancer. + * + * @param mAppName The name of the application. It will be used to customize + * the About and Quit menu items. If you do not wish to customize + * the About and Quit menu items, just pass <tt>null</tt> here. + */ + public MenuBarEnhancerCocoa() { + } + + public MenuBarMode getMenuBarMode() { + return MenuBarMode.MAC_OS; + } + + /** + * Setup the About and Preferences native menut items with the + * given application name and links them to the callback. + * + * @param appName The application name. + * @param display The SWT display. Must not be null. + * @param callbacks The callbacks invoked by the menus. + */ + public void setupMenu( + String appName, + Display display, + IMenuBarCallback callbacks) { + + mAppName = appName; + + // This is our callback object whose 'actionProc' method will be called + // when the About or Preferences menuItem is invoked. + ActionProctarget target = new ActionProctarget(callbacks); + + try { + // Initialize the menuItems. + initialize(target); + } catch (Exception e) { + throw new IllegalStateException(e); + } + + // Schedule disposal of callback object + display.disposeExec(new Runnable() { + public void run() { + invoke(mProc3Args, "dispose"); + } + }); + } + + private void initialize(Object callbackObject) + throws Exception { + + Class<?> osCls = classForName("org.eclipse.swt.internal.cocoa.OS"); + + // Register names in objective-c. + if (mSelAboutMenuItemSelected == 0) { + mSelPreferencesMenuItemSelected = registerName(osCls, "preferencesMenuItemSelected:"); //$NON-NLS-1$ + mSelAboutMenuItemSelected = registerName(osCls, "aboutMenuItemSelected:"); //$NON-NLS-1$ + } + + // Create an SWT Callback object that will invoke the actionProc method + // of our internal callback Object. + mProc3Args = new Callback(callbackObject, "actionProc", 3); //$NON-NLS-1$ + Method getAddress = Callback.class.getMethod("getAddress", new Class[0]); + Object object = getAddress.invoke(mProc3Args, (Object[]) null); + long proc3 = convertToLong(object); + if (proc3 == 0) { + SWT.error(SWT.ERROR_NO_MORE_CALLBACKS); + } + + Class<?> nsMenuCls = classForName("org.eclipse.swt.internal.cocoa.NSMenu"); + Class<?> nsMenuitemCls = classForName("org.eclipse.swt.internal.cocoa.NSMenuItem"); + Class<?> nsStringCls = classForName("org.eclipse.swt.internal.cocoa.NSString"); + Class<?> nsApplicationCls = classForName("org.eclipse.swt.internal.cocoa.NSApplication"); + + // Instead of creating a new delegate class in objective-c, + // just use the current SWTApplicationDelegate. An instance of this + // is a field of the Cocoa Display object and is already the target + // for the menuItems. So just get this class and add the new methods + // to it. + object = invoke(osCls, "objc_lookUpClass", new Object[] { + "SWTApplicationDelegate" + }); + long cls = convertToLong(object); + + // Add the action callbacks for Preferences and About menu items. + invoke(osCls, "class_addMethod", + new Object[] { + wrapPointer(cls), + wrapPointer(mSelPreferencesMenuItemSelected), + wrapPointer(proc3), "@:@"}); //$NON-NLS-1$ + invoke(osCls, "class_addMethod", + new Object[] { + wrapPointer(cls), + wrapPointer(mSelAboutMenuItemSelected), + wrapPointer(proc3), "@:@"}); //$NON-NLS-1$ + + // Get the Mac OS X Application menu. + Object sharedApplication = invoke(nsApplicationCls, "sharedApplication"); + Object mainMenu = invoke(sharedApplication, "mainMenu"); + Object mainMenuItem = invoke(nsMenuCls, mainMenu, "itemAtIndex", new Object[] { + wrapPointer(0) + }); + Object appMenu = invoke(mainMenuItem, "submenu"); + + // Create the About <application-name> menu command + Object aboutMenuItem = + invoke(nsMenuCls, appMenu, "itemAtIndex", new Object[] { + wrapPointer(kAboutMenuItem) + }); + if (mAppName != null) { + Object nsStr = invoke(nsStringCls, "stringWith", new Object[] { + "About " + mAppName + }); + invoke(nsMenuitemCls, aboutMenuItem, "setTitle", new Object[] { + nsStr + }); + } + // Rename the quit action. + if (mAppName != null) { + Object quitMenuItem = + invoke(nsMenuCls, appMenu, "itemAtIndex", new Object[] { + wrapPointer(kQuitMenuItem) + }); + Object nsStr = invoke(nsStringCls, "stringWith", new Object[] { + "Quit " + mAppName + }); + invoke(nsMenuitemCls, quitMenuItem, "setTitle", new Object[] { + nsStr + }); + } + + // Enable the Preferences menuItem. + Object prefMenuItem = + invoke(nsMenuCls, appMenu, "itemAtIndex", new Object[] { + wrapPointer(kPreferencesMenuItem) + }); + invoke(nsMenuitemCls, prefMenuItem, "setEnabled", new Object[] { + true + }); + + // Set the action to execute when the About or Preferences menuItem is + // invoked. + // + // We don't need to set the target here as the current target is the + // SWTApplicationDelegate and we have registerd the new selectors on + // it. So just set the new action to invoke the selector. + invoke(nsMenuitemCls, prefMenuItem, "setAction", + new Object[] { + wrapPointer(mSelPreferencesMenuItemSelected) + }); + invoke(nsMenuitemCls, aboutMenuItem, "setAction", + new Object[] { + wrapPointer(mSelAboutMenuItemSelected) + }); + } + + private long registerName(Class<?> osCls, String name) + throws IllegalArgumentException, SecurityException, IllegalAccessException, + InvocationTargetException, NoSuchMethodException { + Object object = invoke(osCls, "sel_registerName", new Object[] { + name + }); + return convertToLong(object); + } + + private long convertToLong(Object object) { + if (object instanceof Integer) { + Integer i = (Integer) object; + return i.longValue(); + } + if (object instanceof Long) { + Long l = (Long) object; + return l.longValue(); + } + return 0; + } + + private static Object wrapPointer(long value) { + Class<?> PTR_CLASS = C.PTR_SIZEOF == 8 ? long.class : int.class; + if (PTR_CLASS == long.class) { + return new Long(value); + } else { + return new Integer((int) value); + } + } + + private static Object invoke(Class<?> clazz, String methodName, Object[] args) { + return invoke(clazz, null, methodName, args); + } + + private static Object invoke(Class<?> clazz, Object target, String methodName, Object[] args) { + try { + Class<?>[] signature = new Class<?>[args.length]; + for (int i = 0; i < args.length; i++) { + Class<?> thisClass = args[i].getClass(); + if (thisClass == Integer.class) + signature[i] = int.class; + else if (thisClass == Long.class) + signature[i] = long.class; + else if (thisClass == Byte.class) + signature[i] = byte.class; + else if (thisClass == Boolean.class) + signature[i] = boolean.class; + else + signature[i] = thisClass; + } + Method method = clazz.getMethod(methodName, signature); + return method.invoke(target, args); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + private Class<?> classForName(String classname) { + try { + Class<?> cls = Class.forName(classname); + return cls; + } catch (ClassNotFoundException e) { + throw new IllegalStateException(e); + } + } + + private Object invoke(Class<?> cls, String methodName) { + return invoke(cls, methodName, (Class<?>[]) null, (Object[]) null); + } + + private Object invoke(Class<?> cls, String methodName, Class<?>[] paramTypes, + Object... arguments) { + try { + Method m = cls.getDeclaredMethod(methodName, paramTypes); + return m.invoke(null, arguments); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + private Object invoke(Object obj, String methodName) { + return invoke(obj, methodName, (Class<?>[]) null, (Object[]) null); + } + + private Object invoke(Object obj, String methodName, Class<?>[] paramTypes, Object... arguments) { + try { + Method m = obj.getClass().getDeclaredMethod(methodName, paramTypes); + return m.invoke(obj, arguments); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } +} diff --git a/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCocoa.java.txt b/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCocoa.java.txt deleted file mode 100755 index 591f253..0000000 --- a/swtmenubar/src-darwin/com/android/menubar/internal/MenuBarEnhancerCocoa.java.txt +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (C) 2011 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.menubar.internal; - -import com.android.menubar.IMenuBarCallback; -import com.android.menubar.IMenuBarEnhancer; -import com.android.menubar.MenuBarEnhancer; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.internal.C; -import org.eclipse.swt.internal.Callback; -import org.eclipse.swt.internal.cocoa.NSApplication; -import org.eclipse.swt.internal.cocoa.NSMenu; -import org.eclipse.swt.internal.cocoa.NSMenuItem; -import org.eclipse.swt.internal.cocoa.NSObject; -import org.eclipse.swt.internal.cocoa.OS; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Menu; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - - -/** - * Implementation of IMenuBarEnhancer for MacOS Cocoa SWT - * <p/> - * Note: this is currently deactivated since we are not currently - * shipping a version of SWT.jar that has cocoa support (only carbon). - */ -public final class MenuBarEnhancerCocoa implements IMenuBarEnhancer { - - private static final int kAboutMenuItem = 0; - private static final int kPreferencesMenuItem = 2; - private static final int kServicesMenuItem = 4; - private static final int kHideApplicationMenuItem = 6; - private static final int kQuitMenuItem = 10; - - private EnhancerDelegate mDelegate; - private long mDelegateJniRef; - private IMenuBarCallback mCallbacks; - - public static class EnhancerDelegate extends NSObject { - public EnhancerDelegate() { - super(); - } - public EnhancerDelegate(int id) { - super(id); - } - - } - static long sel_preferencesMenuItemSelected_; - static long sel_aboutMenuItemSelected_; - - /* This callback is not freed */ - static Callback proc3Args; - static final byte[] SWT_OBJECT = { 'S', 'W', 'T', '_', 'O', 'B', 'J', 'E', 'C', 'T', '\0' }; - - public MenuBarEnhancerCocoa() { - } - - public void setupMenu( - String appName, - Menu swtMenu, - IMenuBarCallback callbacks) { - mCallbacks = callbacks; - final Display display = swtMenu.getDisplay(); - - init1(); - - try { - mDelegate = new EnhancerDelegate(); - mDelegate.alloc().init(); - //call OS.NewGlobalRef - Method method = OS.class.getMethod("NewGlobalRef", new Class[] { Object.class }); - Object object = method.invoke(OS.class, new Object[] { this }); - mDelegateJniRef = convertToLong(object); - } catch (Exception e) { - // theoretically, one of SecurityException, Illegal*Exception, - // InvocationTargetException, NoSuch*Exception - // not expected to happen at all. - log(e); - } - - if (mDelegateJniRef == 0) { - SWT.error(SWT.ERROR_NO_HANDLES); - } - - try { - Field idField = EnhancerDelegate.class.getField("id"); - Object idValue = idField.get(mDelegate); - invokeMethod(OS.class, "object_setInstanceVariable", - new Object[] { idValue, SWT_OBJECT, wrapPointer(mDelegateJniRef) }); - - hookApplicationMenu(appName); - - // schedule disposal of callback object - display.disposeExec(new Runnable() { - public void run() { - if (mDelegateJniRef != 0) { - try { - invokeMethod(OS.class, "DeleteGlobalRef", new Object[] { wrapPointer(mDelegateJniRef) }); - } catch (Exception e) { - // theoretically, one of SecurityException,Illegal*Exception,InvocationTargetException,NoSuch*Exception - // not expected to happen at all. - log(e); - } - } - mDelegateJniRef = 0; - - if (mDelegate != null) { - mDelegate.release(); - mDelegate = null; - } - } - }); - } catch (Exception e) { - // theoretically, one of SecurityException,Illegal*Exception,InvocationTargetException,NoSuch*Exception - // not expected to happen at all. - log(e); - } - } - - private long registerName(String name) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { - Class clazz = OS.class; - Object object = invokeMethod(clazz, "sel_registerName", new Object[] {name}); - return convertToLong(object); - } - - private void init1() { - try { - if (sel_aboutMenuItemSelected_ == 0) { - sel_preferencesMenuItemSelected_ = registerName("preferencesMenuItemSelected:"); //$NON-NLS-1$ - sel_aboutMenuItemSelected_ = registerName("aboutMenuItemSelected:"); //$NON-NLS-1$ - init2(); - } - } catch (Exception e) { - // theoretically, one of SecurityException,Illegal*Exception,InvocationTargetException,NoSuch*Exception - // not expected to happen at all. - log(e); - } - } - - private void init2() throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchFieldException { - // TODO: These should either move out of Display or be accessible to this class. - byte[] types = {'*','\0'}; - int size = C.PTR_SIZEOF, align = C.PTR_SIZEOF == 4 ? 2 : 3; - - Class clazz = this.getClass(); - - proc3Args = new Callback(clazz, "actionProc", 3); //$NON-NLS-1$ - //call getAddress - Method getAddress = Callback.class.getMethod("getAddress", new Class[0]); //$NON-NLS-1$ - Object object = getAddress.invoke(proc3Args, null); - long proc3 = convertToLong(object); - if (proc3 == 0) { - SWT.error (SWT.ERROR_NO_MORE_CALLBACKS); - } - - //call objc_allocateClassPair - Field field = OS.class.getField("class_NSObject"); //$NON-NLS-1$ - Object fieldObj = field.get(OS.class); - object = invokeMethod(OS.class, "objc_allocateClassPair", //$NON-NLS-1$ - new Object[] { fieldObj, "SWTCocoaEnhancerDelegate", wrapPointer(0) }); //$NON-NLS-1$ - long cls = convertToLong(object); - - invokeMethod(OS.class, "class_addIvar", new Object[] { //$NON-NLS-1$ - wrapPointer(cls), SWT_OBJECT, wrapPointer(size), - new Byte((byte) align), types }); - - // Add the action callback - invokeMethod(OS.class, "class_addMethod", new Object[] { //$NON-NLS-1$ - wrapPointer(cls), - wrapPointer(sel_preferencesMenuItemSelected_), - wrapPointer(proc3), "@:@" }); //$NON-NLS-1$ - invokeMethod(OS.class, "class_addMethod", new Object[] { //$NON-NLS-1$ - wrapPointer(cls), - wrapPointer(sel_aboutMenuItemSelected_), - wrapPointer(proc3), "@:@" }); //$NON-NLS-1$ - - invokeMethod(OS.class, "objc_registerClassPair", //$NON-NLS-1$ - new Object[] { wrapPointer(cls) }); - } - - private void log(Exception e) { - mCallbacks.printError("%1$s: %2$s", getClass().getSimpleName(), e.toString()); //$NON-NLS-1$ - } - - private void hookApplicationMenu(String appName) { - try { - // create About Eclipse menu command - NSMenu mainMenu = NSApplication.sharedApplication().mainMenu(); - NSMenuItem mainMenuItem = (NSMenuItem) invokeMethod(NSMenu.class, mainMenu, - "itemAtIndex", new Object[] {wrapPointer(0)}); //$NON-NLS-1$ - NSMenu appMenu = mainMenuItem.submenu(); - - // add the about action - NSMenuItem aboutMenuItem = (NSMenuItem) invokeMethod(NSMenu.class, appMenu, - "itemAtIndex", new Object[] {wrapPointer(kAboutMenuItem)}); - aboutMenuItem.setTitle(NSString.stringWith("About " + appName)); //$NON-NLS-1$ - - // enable pref menu - NSMenuItem prefMenuItem = (NSMenuItem) invokeMethod(NSMenu.class, appMenu, - "itemAtIndex", new Object[] {wrapPointer(kPreferencesMenuItem)}); //$NON-NLS-1$ - prefMenuItem.setEnabled(true); - - // disable services menu - NSMenuItem servicesMenuItem = (NSMenuItem) invokeMethod(NSMenu.class, appMenu, - "itemAtIndex", new Object[] {wrapPointer(kServicesMenuItem)}); //$NON-NLS-1$ - servicesMenuItem.setEnabled(false); - - // Register as a target on the prefs and quit items. - prefMenuItem.setTarget(mDelegate); - invokeMethod(NSMenuItem.class, prefMenuItem, - "setAction", new Object[] {wrapPointer(sel_preferencesMenuItemSelected_)}); //$NON-NLS-1$ - aboutMenuItem.setTarget(mDelegate); - invokeMethod(NSMenuItem.class, aboutMenuItem, - "setAction", new Object[] {wrapPointer(sel_aboutMenuItemSelected_)}); //$NON-NLS-1$ - } catch (Exception e) { - // theoretically, one of SecurityException,Illegal*Exception,InvocationTargetException,NoSuch*Exception - // not expected to happen at all. - log(e); - } - } - - void preferencesMenuItemSelected() { - try { - NSMenu mainMenu = NSApplication.sharedApplication().mainMenu(); - NSMenuItem mainMenuItem = (NSMenuItem) invokeMethod(NSMenu.class, mainMenu, - "itemAtIndex", new Object[] {wrapPointer(0)}); //$NON-NLS-1$ - NSMenu appMenu = mainMenuItem.submenu(); - NSMenuItem prefMenuItem = (NSMenuItem) invokeMethod(NSMenu.class, appMenu, "" + //$NON-NLS-1$ - "itemAtIndex", new Object[] {wrapPointer(kPreferencesMenuItem)}); //$NON-NLS-1$ - try { - prefMenuItem.setEnabled(false); - - mCallbacks.onPreferencesMenuSelected(); - } - finally { - prefMenuItem.setEnabled(true); - } - } catch (Exception e) { - // theoretically, one of SecurityException,Illegal*Exception,InvocationTargetException,NoSuch*Exception - // not expected to happen at all. - log(e); - } - } - - void aboutMenuItemSelected() { - try { - NSMenu mainMenu = NSApplication.sharedApplication().mainMenu(); - NSMenuItem mainMenuItem = (NSMenuItem) invokeMethod(NSMenu.class, mainMenu, - "itemAtIndex", new Object[] {wrapPointer(0)}); //$NON-NLS-1$ - NSMenu appMenu = mainMenuItem.submenu(); - NSMenuItem aboutMenuItem = (NSMenuItem) invokeMethod(NSMenu.class, appMenu, - "itemAtIndex", new Object[] {wrapPointer(kAboutMenuItem)}); //$NON-NLS-1$ - try { - aboutMenuItem.setEnabled(false); - - mCallbacks.onAboutMenuSelected(); - } - finally { - aboutMenuItem.setEnabled(true); - } - } catch (Exception e) { - // theoretically, one of SecurityException,Illegal*Exception,InvocationTargetException,NoSuch*Exception - // not expected to happen at all. - log(e); - } - } - - private long convertToLong(Object object) { - if (object instanceof Integer) { - Integer i = (Integer) object; - return i.longValue(); - } - if (object instanceof Long) { - Long l = (Long) object; - return l.longValue(); - } - return 0; - } - - private static Object invokeMethod(Class clazz, String methodName, - Object[] args) throws IllegalArgumentException, - IllegalAccessException, InvocationTargetException, - SecurityException, NoSuchMethodException { - return invokeMethod(clazz, null, methodName, args); - } - - private static Object invokeMethod(Class clazz, Object target, - String methodName, Object[] args) throws IllegalArgumentException, - IllegalAccessException, InvocationTargetException, - SecurityException, NoSuchMethodException { - Class[] signature = new Class[args.length]; - for (int i = 0; i < args.length; i++) { - Class thisClass = args[i].getClass(); - if (thisClass == Integer.class) { - signature[i] = int.class; - } else if (thisClass == Long.class) { - signature[i] = long.class; - } else if (thisClass == Byte.class) { - signature[i] = byte.class; - } else { - signature[i] = thisClass; - } - } - Method method = clazz.getMethod(methodName, signature); - return method.invoke(target, args); - } - - private static Object wrapPointer(long value) { - Class PTR_CLASS = C.PTR_SIZEOF == 8 ? long.class : int.class; - if (PTR_CLASS == long.class) { - return new Long(value); - } else { - return new Integer((int)value); - } - } -} diff --git a/swtmenubar/src/com/android/menubar/IMenuBarEnhancer.java b/swtmenubar/src/com/android/menubar/IMenuBarEnhancer.java index 1d587e9..d835bd6 100644 --- a/swtmenubar/src/com/android/menubar/IMenuBarEnhancer.java +++ b/swtmenubar/src/com/android/menubar/IMenuBarEnhancer.java @@ -16,6 +16,7 @@ package com.android.menubar; +import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Menu; @@ -25,6 +26,33 @@ import org.eclipse.swt.widgets.Menu; */ public interface IMenuBarEnhancer { + /** Values that indicate how the menu bar is being handlded. */ + public enum MenuBarMode { + /** + * The Mac-specific About and Preferences are being used. + * No File > Exit menu should be provided by the application. + */ + MAC_OS, + /** + * The provided SWT {@link Menu} is being used for About and Options. + * The application should provide a File > Exit menu. + */ + GENERIC + } + + /** + * Returns a {@link MenuBarMode} enum that indicates how the menu bar is going to + * or has been modified. This is implementation specific and can be called before or + * after {@link #setupMenu}. + * <p/> + * Callers would typically call that to know if they need to hide or display + * menu items. For example when {@link MenuBarMode#MAC_OS} is used, an app + * would typically not need to provide any "File > Exit" menu item. + * + * @return One of the {@link MenuBarMode} values. + */ + public MenuBarMode getMenuBarMode(); + /** * Updates the menu bar to provide an About menu item and a Preferences menu item. * Depending on the platform, the menu items might be decorated with the @@ -34,13 +62,12 @@ public interface IMenuBarEnhancer { * {@link MenuBarEnhancer#setupMenu} should be used instead. * * @param appName Name used for the About menu item and similar. Must not be null. - * @param swtMenu For non-mac platform this is the menu where the "About" and - * the "Preferences" menu items are created. Must not be null. + * @param display The SWT display. Must not be null. * @param callbacks Callbacks called when "About" and "Preferences" menu items are invoked. * Must not be null. */ public void setupMenu( String appName, - Menu swtMenu, + Display display, IMenuBarCallback callbacks); } diff --git a/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java b/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java index e40fbe0..eb3e817 100644 --- a/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java +++ b/swtmenubar/src/com/android/menubar/MenuBarEnhancer.java @@ -16,9 +16,13 @@ package com.android.menubar; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.action.Separator; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; @@ -57,49 +61,33 @@ public final class MenuBarEnhancer { */ public static IMenuBarEnhancer setupMenu( String appName, - Menu swtMenu, + final Menu swtMenu, IMenuBarCallback callbacks) { - IMenuBarEnhancer enhancer = null; - String p = SWT.getPlatform(); - String className = null; - if ("carbon".equals(p)) { //$NON-NLS-1$ - className = "com.android.menubar.internal.MenuBarEnhancerCarbon"; //$NON-NLS-1$ - } else if ("cocoa".equals(p)) { //$NON-NLS-1$ - // Note: we have a Cocoa implementation that is currently disabled - // since the SWT.jar that we use only contain Carbon implementations. - // - // className = "com.android.menubar.internal.MenuBarEnhancerCocoa"; //$NON-NLS-1$ - } - - if (className != null) { - try { - Class<?> clazz = p.getClass().forName(className); - enhancer = (IMenuBarEnhancer) clazz.newInstance(); - } catch (Exception e) { - // Log an error and fallback on the default implementation. - callbacks.printError( - "Failed to instantiate %1$s: %2$s", //$NON-NLS-1$ - className, - e.toString()); - } - } + IMenuBarEnhancer enhancer = getEnhancer(callbacks); - // Default implementation for other platforms + // Default implementation for generic platforms if (enhancer == null) { enhancer = new IMenuBarEnhancer() { + + public MenuBarMode getMenuBarMode() { + return MenuBarMode.GENERIC; + } + public void setupMenu( String appName, - Menu menu, + Display display, final IMenuBarCallback callbacks) { - new MenuItem(menu, SWT.SEPARATOR); + if (swtMenu.getItemCount() > 0) { + new MenuItem(swtMenu, SWT.SEPARATOR); + } // Note: we use "Preferences" on Mac and "Options" on Windows/Linux. - final MenuItem pref = new MenuItem(menu, SWT.NONE); - pref.setText("Options..."); + final MenuItem pref = new MenuItem(swtMenu, SWT.NONE); + pref.setText("&Options..."); - final MenuItem about = new MenuItem(menu, SWT.NONE); - about.setText("About..."); + final MenuItem about = new MenuItem(swtMenu, SWT.NONE); + about.setText("&About..."); pref.addSelectionListener(new SelectionAdapter() { @Override @@ -130,8 +118,101 @@ public final class MenuBarEnhancer { }; } - enhancer.setupMenu(appName, swtMenu, callbacks); + enhancer.setupMenu(appName, swtMenu.getDisplay(), callbacks); + return enhancer; + } + + + public static IMenuBarEnhancer setupMenuManager( + String appName, + Display display, + final IMenuManager menuManager, + final IAction aboutAction, + final IAction preferencesAction, + final IAction quitAction) { + + IMenuBarCallback callbacks = new IMenuBarCallback() { + public void printError(String format, Object... args) { + System.err.println(String.format(format, args)); + } + + public void onPreferencesMenuSelected() { + if (preferencesAction != null) { + preferencesAction.run(); + } + } + + public void onAboutMenuSelected() { + if (aboutAction != null) { + aboutAction.run(); + } + } + }; + + IMenuBarEnhancer enhancer = getEnhancer(callbacks); + + // Default implementation for generic platforms + if (enhancer == null) { + enhancer = new IMenuBarEnhancer() { + + public MenuBarMode getMenuBarMode() { + return MenuBarMode.GENERIC; + } + + public void setupMenu( + String appName, + Display display, + final IMenuBarCallback callbacks) { + if (!menuManager.isEmpty()) { + menuManager.add(new Separator()); + } + + if (aboutAction != null) { + menuManager.add(aboutAction); + } + if (preferencesAction != null) { + menuManager.add(preferencesAction); + } + if (quitAction != null) { + if (aboutAction != null || preferencesAction != null) { + menuManager.add(new Separator()); + } + menuManager.add(quitAction); + } + } + }; + } + + enhancer.setupMenu(appName, display, callbacks); return enhancer; } + private static IMenuBarEnhancer getEnhancer(IMenuBarCallback callbacks) { + IMenuBarEnhancer enhancer = null; + String p = SWT.getPlatform(); + String className = null; + if ("carbon".equals(p)) { //$NON-NLS-1$ + className = "com.android.menubar.internal.MenuBarEnhancerCarbon"; //$NON-NLS-1$ + } else if ("cocoa".equals(p)) { //$NON-NLS-1$ + className = "com.android.menubar.internal.MenuBarEnhancerCocoa"; //$NON-NLS-1$ + } + + if (System.getenv("DEBUG_SWTMENUBAR") != null) { + callbacks.printError("DEBUG SwtMenuBar: SWT=%1$s, class=%2$s", p, className); + } + + if (className != null) { + try { + Class<?> clazz = Class.forName(className); + enhancer = (IMenuBarEnhancer) clazz.newInstance(); + } catch (Exception e) { + // Log an error and fallback on the default implementation. + callbacks.printError( + "Failed to instantiate %1$s: %2$s", //$NON-NLS-1$ + className, + e.toString()); + } + } + return enhancer; + } } |