summaryrefslogtreecommitdiff
path: root/src/plugins/emulator/src/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/emulator/src/com')
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/EmulatorPlugin.java456
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/devfrm/DeviceFrameworkManager.java315
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/devfrm/IDeviceFrameworkSupport.java46
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/AbstractEmuCtProvider.java248
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/AbstractEmuLabelProvider.java286
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerLeafNode.java71
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerNode.java152
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerRootNode.java94
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/IEmuIconPath.java30
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/SrcDestComposite.java439
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceNotFoundException.java58
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceStartException.java57
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceStopException.java57
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/SkinException.java58
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/StartCancelledException.java58
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/StartTimeoutException.java58
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/AbstractInputLogic.java55
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IAndroidEmulatorInstance.java228
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IEmulatorView.java36
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IInputLogic.java56
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/AndroidPressKey.java251
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/AndroidSkinBean.java123
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/IAndroidKey.java57
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/IAndroidSkin.java163
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/ISkinFrameworkConstants.java33
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/ISkinKeyXmlTags.java90
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/SkinFramework.java219
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/EmulatorCoreUtils.java175
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/TelnetAndroidInput.java416
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/VncAndroidInput.java96
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/AndroidDeviceHandler.java61
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/AndroidDeviceUtils.java74
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/CreateAVDOnStartupListener.java73
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/EmulatorDropSupportHandler.java37
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/IAndroidDeviceConstants.java30
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/IDevicePropertiesConstants.java132
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/SequoyahInstanceBackward.java78
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/SequoyahLogRedirector.java237
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/TmLDeviceFrameworkSupport.java88
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/AndroidEmuDefBean.java102
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/AndroidEmuDefMgr.java265
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/IAndroidEmuDefConstants.java42
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/handlers/OpenNewDeviceWizardHandler.java52
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/init/InitServiceHandler.java145
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDevInstBuilder.java108
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDevInstListener.java130
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDeviceInstance.java776
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/IStartupOptionsConstants.java155
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOption.java307
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOptionsGroup.java106
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOptionsMgt.java785
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/refresh/InstancesListRefresh.java184
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/refresh/InstancesListRefreshHandler.java39
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/sync/DeviceViewsSync.java326
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AbstractPropertiesComposite.java249
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AndroidPropertiesPage.java185
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AndroidPropertiesStartupOptionsPage.java228
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/DevicePropertiesPage.java47
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/DpiScaleCalculatorDialog.java383
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/InfoComposite.java210
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/PropertiesMainComposite.java1287
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/StartupOptionsComposite.java435
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardMainPage.java445
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardMainPageOperation.java76
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardStartupOptionsPage.java195
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/i18n/EmulatorNLS.java428
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/i18n/emulatorNLS.properties173
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AbstractStartAndroidEmulatorLogic.java69
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AndroidExceptionHandler.java392
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AndroidLogicUtils.java360
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/ConnectVncLogic.java227
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/ForwardVncPortLogic.java55
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/IAndroidLogic.java30
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/IAndroidLogicInstance.java103
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/StartEmulatorProcessLogic.java523
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/StartVncServerLogic.java229
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/TransferFilesLogic.java97
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/reset/AndroidEmulatorReseter.java189
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/start/AndroidEmulatorStarter.java173
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/stop/AndroidEmulatorStopper.java188
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/service/repair/RepairAvdHandler.java107
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/service/reset/ResetServiceHandler.java124
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/service/start/StartEmulatorHandler.java128
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/service/stop/StopEmulatorCommand.java47
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/service/stop/StopEmulatorHandler.java130
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/AndroidSkin.java428
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/AndroidSkinTranslator.java1370
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ILayoutBean.java27
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ILayoutConstants.java78
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ImagePositionBean.java175
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutBean.java231
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutFileModel.java706
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutFileParser.java552
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/PartBean.java154
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/PartRefBean.java149
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/RectangleBean.java127
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/IAndroidUIConstants.java24
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/IUIHelpConstants.java31
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/IAndroidComposite.java80
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/RemoteCLIDisplay.java277
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/UIHelper.java145
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/maindisplay/MainDisplayComposite.java470
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/nativewindow/NativeWindowComposite.java621
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/skin/AndroidSkinLayout.java507
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/skin/SkinComposite.java1300
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/AbstractZoomHandler.java146
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ChangeEmulatorOrientationHandler.java119
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ChangeZoomHandler.java80
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/IHandlerConstants.java69
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ShowViewHandler.java79
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ZoomInOutHandler.java90
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/AndroidEmulatorPerspective.java254
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/AndroidPerspectiveExtensionBean.java98
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/AndroidPerspectiveExtensionReader.java90
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/IAndroidPerspectiveExtensionConstants.java39
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AbstractAndroidView.java1839
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AndroidView.java265
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AndroidViewData.java167
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/LayoutContributionItem.java143
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/MainDisplayView.java224
-rw-r--r--src/plugins/emulator/src/com/motorola/studio/android/emulator10/StartAndroidEmulatorLogic.java117
121 files changed, 27596 insertions, 0 deletions
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/EmulatorPlugin.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/EmulatorPlugin.java
new file mode 100644
index 0000000..eaf7512
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/EmulatorPlugin.java
@@ -0,0 +1,456 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.List;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.device.common.utilities.BasePlugin;
+import org.eclipse.sequoyah.device.framework.events.IInstanceListener;
+import org.eclipse.sequoyah.device.framework.events.InstanceAdapter;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent.InstanceEventType;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.factory.DeviceTypeRegistry;
+import org.eclipse.sequoyah.device.framework.model.IDeviceType;
+import org.eclipse.sequoyah.device.framework.model.IService;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+import org.eclipse.sequoyah.device.framework.ui.DeviceUIPlugin;
+import org.eclipse.sequoyah.device.framework.ui.view.InstanceMgtView;
+import org.eclipse.sequoyah.device.framework.ui.wizard.DefaultDeviceTypeMenuWizardPage;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.DdmsRunnable;
+import com.motorola.studio.android.adt.StudioAndroidEventManager;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.device.AndroidDeviceUtils;
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.device.SequoyahLogRedirector;
+import com.motorola.studio.android.emulator.device.instance.AndroidDevInstListener;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+import com.motorola.studio.android.emulator.device.refresh.InstancesListRefresh;
+import com.motorola.studio.android.emulator.device.sync.DeviceViewsSync;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class EmulatorPlugin extends AbstractUIPlugin
+{
+ // The plug-in ID
+ public static final String PLUGIN_ID = "com.motorola.studio.android.emulator";
+
+ // The shared instance
+ private static EmulatorPlugin plugin;
+
+ // The ID of the device declared by this plug-in
+ public static final String DEVICE_ID = PLUGIN_ID + ".androidDevice";
+
+ // The ID of all the status declared by this plug-in
+ public static final String STATUS_ONLINE_ID = PLUGIN_ID + ".status.online";
+
+ public static final String STATUS_OFFLINE_NO_DATA = PLUGIN_ID + ".status.offlineNoData";
+
+ public static final String STATUS_OFFLINE = PLUGIN_ID + ".status.offline";
+
+ public static final String STATUS_NOT_AVAILABLE = PLUGIN_ID + ".status.notavailable";
+
+ public static final String SERVICE_INIT_ID = PLUGIN_ID + ".initEmulatorService";
+
+ public static final String STOP_SERVICE_ID = PLUGIN_ID + ".stopService";
+
+ public static final String START_SERVICE_ID = PLUGIN_ID + ".startService";
+
+ private static final String DEV_MANAGER_HELP = DeviceUIPlugin.PLUGIN_ID + ".devmgr";
+
+ private static final String NEW_DEVICE_HELP = DeviceUIPlugin.PLUGIN_ID + ".newdev";
+
+ /**
+ * Reference the id of the extension point with the default Android Emulator definitions...
+ */
+ public static String DEFAULT_EMULATOR_DEFINITION =
+ "com.motorola.studio.android.emulator10.defaultEmulatorDefinitions";
+
+ public static final String FORCE_ATTR = "force";
+
+ public static final String EMULATOR_UNEXPECTEDLY_STOPPED = "emulator.unexpectedly.stopped";
+
+ private static AndroidDevInstListener instanceListener;
+
+ private static DdmsRunnable connectedListener = new DdmsRunnable()
+ {
+ @Override
+ public void run(String serialNumber)
+ {
+ if (DDMSFacade.isEmulator(serialNumber))
+ {
+ InstancesListRefresh.refresh();
+
+ info("New Device connected at " + serialNumber);
+
+ String vmName = DDMSFacade.getNameBySerialNumber(serialNumber);
+
+ if (vmName != null)
+ {
+ DeviceFrameworkManager devFrameworkManager =
+ DeviceFrameworkManager.getInstance();
+
+ IAndroidEmulatorInstance instance =
+ devFrameworkManager.getInstanceByName(vmName);
+
+ if (instance instanceof AndroidDeviceInstance)
+ {
+ final AndroidDeviceInstance emulatorInstance =
+ (AndroidDeviceInstance) instance;
+
+ AndroidDeviceUtils.fireDummyStartTransition(emulatorInstance, serialNumber);
+
+ }
+ }
+ }
+ }
+ };
+
+ private static DdmsRunnable disconnectedListener = new DdmsRunnable()
+ {
+ @Override
+ public void run(String serialNum)
+ {
+ if (DDMSFacade.isEmulator(serialNum))
+ {
+ info("Device just disconnected from serial=" + serialNum);
+
+ String vmName = DDMSFacade.getNameBySerialNumber(serialNum);
+
+ if (vmName != null)
+ {
+ IAndroidEmulatorInstance instance =
+ DeviceFrameworkManager.getInstance().getInstanceByName(vmName);
+
+ if ((instance != null) && (instance.isStarted()))
+ {
+ try
+ {
+ instance.stop(true);
+ DialogWithToggleUtils.showError(EMULATOR_UNEXPECTEDLY_STOPPED,
+ EmulatorNLS.GEN_Error, NLS.bind(
+ EmulatorNLS.ERR_AndroidLogicPlugin_EmulatorStopped,
+ instance.getName()));
+
+ }
+ catch (Exception e)
+ {
+ error("Error trying to force the stop process on instance associated to disconnected device: "
+ + instance);
+ }
+ }
+
+ if (instance instanceof AndroidDeviceInstance)
+ {
+ ((AndroidDeviceInstance) instance).setNameSuffix(null);
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED,
+ (AndroidDeviceInstance) instance));
+ }
+ }
+ else
+ {
+ // This block is executed if we get a vmName == null condition. This can happen if
+ // ADT updates the device in a way that it makes the name not accessible.
+ //
+ // What is needed to be done in such a case is to iterate on all TmL instances, looking for
+ // objects that contain serialNumber as the instance suffix. This guarantees that we will not
+ // leave a not consistent serial number being displayed at the Instance Management view.
+
+ for (IAndroidEmulatorInstance instance : DeviceFrameworkManager.getInstance()
+ .getAllInstances())
+ {
+ if (instance instanceof AndroidDeviceInstance)
+ {
+ AndroidDeviceInstance androidInstance =
+ (AndroidDeviceInstance) instance;
+ String instanceSuffix = androidInstance.getNameSuffix();
+
+ if ((instanceSuffix != null) && instanceSuffix.equals(serialNum))
+ {
+ androidInstance.setNameSuffix(null);
+
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED,
+ androidInstance));
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+
+ private static final Runnable sdkLoaderListener = new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ InstancesListRefresh.refresh();
+ if (!Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ IPreferenceStore store = getDefault().getPreferenceStore();
+ boolean deviceStartupOptionsUpdated =
+ store.getBoolean("DeviceStartupOptionsUpdated");
+ if (!deviceStartupOptionsUpdated)
+ {
+ for (IAndroidEmulatorInstance instance : DeviceFrameworkManager.getInstance()
+ .getAllInstances())
+ {
+ if (instance instanceof AndroidDeviceInstance)
+ {
+ AndroidDeviceInstance androidInstance =
+ (AndroidDeviceInstance) instance;
+
+ Properties emuProperties = androidInstance.getProperties();
+
+ String commandline =
+ emuProperties.getProperty(
+ IDevicePropertiesConstants.commandline, "");
+ if (commandline.contains("-no-window"))
+ {
+ commandline = commandline.replace("-no-window", "");
+ }
+ emuProperties.setProperty(IDevicePropertiesConstants.commandline,
+ commandline);
+ androidInstance.setProperties(emuProperties);
+
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED,
+ androidInstance));
+ }
+ }
+ store.setValue("DeviceStartupOptionsUpdated", true);
+ }
+ }
+ }
+ };
+
+ private static IInstanceListener sequoyahInstanceListener = new InstanceAdapter()
+ {
+ @Override
+ public void instanceUpdated(InstanceEvent e)
+ {
+ AbstractAndroidView.updateInstanceName(e.getInstance());
+ }
+ };
+
+ private static ServiceHandler stopServiceHandler = null;
+
+ private static ServiceHandler startServiceHandler = null;
+
+ private static String stopServiceId = null;
+
+ private static String startServiceId = null;
+
+ /**
+ * The constructor
+ */
+ public EmulatorPlugin()
+ {
+ plugin = this;
+ }
+
+ /**
+ * Activates the plug-in and initializes the logger
+ *
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void start(BundleContext context) throws Exception
+ {
+ StudioLogger.debug(EmulatorPlugin.class, "Starting MOTODEV Android Emulator Plugin...");
+
+ super.start(context);
+
+ start();
+
+ StudioLogger.debug(EmulatorPlugin.class, "MOTODEV Android Emulator Plugin started.");
+ }
+
+ private void start()
+ {
+ // Setting the TmL logger to redirect logs to the logger controlled
+ // by this class
+ SequoyahLogRedirector tmlLogger = new SequoyahLogRedirector();
+ org.eclipse.sequoyah.vnc.utilities.logger.Logger.setLogger(tmlLogger);
+ BasePlugin.getBaseDefault().setLogger(tmlLogger);
+
+ instanceListener = new AndroidDevInstListener();
+ InstanceEventManager.getInstance().addInstanceListener(instanceListener);
+ StudioAndroidEventManager.asyncAddDeviceChangeListeners(connectedListener,
+ disconnectedListener);
+
+ AndroidPlugin.getDefault().addSDKLoaderListener(sdkLoaderListener);
+ // Emulator Views synchronization
+ DeviceViewsSync.getInstance().initialize();
+ // Setting context sensitive help IDs for the TmL screens we use
+ DefaultDeviceTypeMenuWizardPage.setHelpContextId(NEW_DEVICE_HELP);
+ InstanceMgtView.setHelp(DEV_MANAGER_HELP);
+ InstanceEventManager.getInstance().addInstanceListener(sequoyahInstanceListener);
+ registerStopServiceId(STOP_SERVICE_ID);
+ registerStartServiceId(START_SERVICE_ID);
+ }
+
+ /**
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ @Override
+ public void stop(BundleContext context) throws Exception
+ {
+ AndroidPlugin.getDefault().removeSDKLoaderListener(sdkLoaderListener);
+ InstanceEventManager.getInstance().removeInstanceListener(instanceListener);
+ StudioAndroidEventManager.asyncRemoveDeviceChangeListeners(connectedListener,
+ disconnectedListener);
+ InstanceEventManager.getInstance().removeInstanceListener(sequoyahInstanceListener);
+ unregisterStopServiceHandler();
+ unregisterStartServiceHandler();
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Registers a stop service id, through which the stop service handler will be found and
+ * used to delegate stop action of the instances
+ * if possible
+ *
+ * @param stopServiceId The stop service id to be registered
+ */
+ public static void registerStopServiceId(String stopServiceId)
+ {
+ EmulatorPlugin.stopServiceId = stopServiceId;
+ }
+
+ /**
+ * Unregisters the current stop service handler and stop service id.
+ *
+ * After this method is called, it will not be possible for the instance class to delegate the
+ * stop action to a handler.
+ */
+ public static void unregisterStopServiceHandler()
+ {
+ stopServiceHandler = null;
+ stopServiceId = null;
+ }
+
+ /**
+ * Retrieves the stop service handler.
+ *
+ * @return The currently registered stop service handler, or <null> if no handler is registered.
+ */
+ public static ServiceHandler getStopServiceHandler()
+ {
+ if ((stopServiceHandler == null) && (stopServiceId != null))
+ {
+ // find the appropriate stop service handler
+ IDeviceType device =
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(EmulatorPlugin.DEVICE_ID);
+ List<IService> services = device.getServices();
+ for (IService service : services)
+ {
+ IServiceHandler handler = service.getHandler();
+ if (handler.getService().getId().equals(stopServiceId))
+ {
+ stopServiceHandler = (ServiceHandler) handler;
+ break;
+ }
+ }
+ }
+
+ return stopServiceHandler;
+ }
+
+ /**
+ * Registers a start service id, through which the stop service handler will be found and
+ * used to delegate start action of the instances
+ * if possible
+ *
+ * @param stopServiceId The stop service id to be registered
+ */
+ public static void registerStartServiceId(String startServiceId)
+ {
+ EmulatorPlugin.startServiceId = startServiceId;
+ }
+
+ /**
+ * Unregisters the current start service handler and stop service id.
+ *
+ * After this method is called, it will not be possible for the instance class to delegate the
+ * start action to a handler.
+ */
+ public static void unregisterStartServiceHandler()
+ {
+ startServiceHandler = null;
+ startServiceId = null;
+ }
+
+ /**
+ * Retrieves the start service handler.
+ *
+ * @return The currently registered start service handler, or <null> if no handler is registered.
+ */
+ public static ServiceHandler getStartServiceHandler()
+ {
+ if ((startServiceHandler == null) && (startServiceId != null))
+ {
+ // find the appropriate stop service handler
+ IDeviceType device =
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(EmulatorPlugin.DEVICE_ID);
+ List<IService> services = device.getServices();
+ for (IService service : services)
+ {
+ IServiceHandler handler = service.getHandler();
+ if (handler.getService().getId().equals(startServiceId))
+ {
+ startServiceHandler = (ServiceHandler) handler;
+ break;
+ }
+ }
+ }
+
+ return startServiceHandler;
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static EmulatorPlugin getDefault()
+ {
+ return plugin;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/devfrm/DeviceFrameworkManager.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/devfrm/DeviceFrameworkManager.java
new file mode 100644
index 0000000..401d3ff
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/devfrm/DeviceFrameworkManager.java
@@ -0,0 +1,315 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.devfrm;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+
+/**
+ * DESCRIPTION:
+ * This class manages the device frameworks that extend the deviceFramework
+ * extension
+ *
+ * RESPONSIBILITY:
+ * Retrieve all deviceFramework extension data and provide a compiled
+ * view of the information provided by each extension implementer
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use each public method to get the desired information
+ */
+public class DeviceFrameworkManager
+{
+ /*
+ * Extension point related ids section
+ */
+ private static final String DEV_FRAMEWORK_EXTENSION_POINT_ID =
+ EmulatorPlugin.PLUGIN_ID + ".deviceFramework";
+
+ private static final String DEV_FRAMEWORK_ELEM = "deviceFramework";
+
+ private static final String DEV_FRAMEWORK_IMPL_CLASS_ATTR = "class";
+
+ /**
+ * This is a singleton class. The instance is stored in this attribute
+ */
+ private static DeviceFrameworkManager instance;
+
+ /**
+ * A collection containing the classes provided by each extension
+ * implementer for retrieving framework data
+ */
+ private Collection<IDeviceFrameworkSupport> allFrameworks =
+ new HashSet<IDeviceFrameworkSupport>();
+
+ /**
+ * Singleton private constructor
+ */
+ private DeviceFrameworkManager()
+ {
+ populateModel();
+ }
+
+ /**
+ * Gets the instance of the class
+ *
+ * @return The instance of the class
+ */
+ public static DeviceFrameworkManager getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new DeviceFrameworkManager();
+ }
+
+ return instance;
+ }
+
+ /**
+ * Retrieves all instances managed by every device framework
+ * which contributes with the deviceFramework extension point
+ *
+ * @return A collection containing all instances from all frameworks
+ */
+ public Collection<IAndroidEmulatorInstance> getAllInstances()
+ {
+ Collection<IAndroidEmulatorInstance> allInstancesSet =
+ new LinkedHashSet<IAndroidEmulatorInstance>();
+ for (IDeviceFrameworkSupport devFramework : allFrameworks)
+ {
+ Collection<IAndroidEmulatorInstance> devFrmInstances = devFramework.getAllInstances();
+ if (devFrmInstances != null)
+ {
+ allInstancesSet.addAll(devFrmInstances);
+ }
+ }
+
+ return allInstancesSet;
+ }
+
+ /**
+ * Retrieve all registered and available instances
+ *
+ * @return List containing all registered and available instances
+ */
+ public Collection<IAndroidEmulatorInstance> getAvailableInstances()
+ {
+ Collection<IAndroidEmulatorInstance> allInstances = getAllInstances();
+ Collection<IAndroidEmulatorInstance> enabledInstances =
+ new ArrayList<IAndroidEmulatorInstance>(allInstances.size());
+
+ for (IAndroidEmulatorInstance emulatorInstance : allInstances)
+ {
+ if (emulatorInstance.isAvailable())
+ {
+ enabledInstances.add(emulatorInstance);
+ }
+ }
+ return enabledInstances;
+ }
+
+ /**
+ * Retrieve a collection of names of all the IAndroidEmulatorInstance
+ * of all Device frameworks...
+ * @return A collection of all instances of IAndroidEmulatorInstance.
+ */
+ public Collection<String> getAllInstanceNames()
+ {
+ Collection<String> allInstancesNames = new LinkedHashSet<String>();
+ for (IDeviceFrameworkSupport devFramework : allFrameworks)
+ {
+ for (IAndroidEmulatorInstance instance : devFramework.getAllInstances())
+ {
+ allInstancesNames.add(instance.getName());
+ }
+ }
+
+ return allInstancesNames;
+
+ }
+
+ /**
+ * Retrieves the first occurrence of a IAndroidEmulatorInstance with the given name
+ * provided by any framework.
+ * @param name of the emulator instance to be retrieved.
+ * @return reference to a IAndroidEmulatorInstance with the given name or a null
+ * is there are no emulator instance with the given name.
+ */
+ public IAndroidEmulatorInstance getInstanceByName(String name)
+ {
+ IAndroidEmulatorInstance instanceToReturn = null;
+
+ for (IAndroidEmulatorInstance instance : getAllInstances())
+ {
+ if (Platform.getOS().equals(Platform.WS_WIN32))
+ {
+ if (instance.getName().toLowerCase().equals(name.toLowerCase()))
+ {
+ instanceToReturn = instance;
+ break;
+ }
+ }
+ else
+ {
+ if (instance.getName().equals(name))
+ {
+ instanceToReturn = instance;
+ break;
+ }
+ }
+
+ }
+ return instanceToReturn;
+
+ }
+
+ /**
+ * Retrieves all <b>started</b> instances managed by every device framework
+ * which contributes with the deviceFramework extension point
+ *
+ * @return A collection containing all started instances from all frameworks
+ */
+ public Collection<IAndroidEmulatorInstance> getAllStartedInstances()
+ {
+ Collection<IAndroidEmulatorInstance> startedInstancesSet =
+ new HashSet<IAndroidEmulatorInstance>();
+ for (IDeviceFrameworkSupport devFramework : allFrameworks)
+ {
+ Collection<IAndroidEmulatorInstance> devFrmInstances = devFramework.getAllInstances();
+ if (devFrmInstances != null)
+ {
+ for (IAndroidEmulatorInstance instance : devFrmInstances)
+ {
+ if (instance.isStarted())
+ {
+ startedInstancesSet.add(instance);
+ }
+ }
+ }
+ }
+
+ return startedInstancesSet;
+ }
+
+ /**
+ * Retrieves all <b>connected</b> instances managed by every device framework
+ * which contributes with the deviceFramework extension point
+ *
+ * @return A collection containing all connected instances from all frameworks
+ */
+ public Collection<IAndroidEmulatorInstance> getAllConnectedInstances()
+ {
+ Collection<IAndroidEmulatorInstance> connectedInstancesSet =
+ new HashSet<IAndroidEmulatorInstance>();
+ for (IDeviceFrameworkSupport devFramework : allFrameworks)
+ {
+ Collection<IAndroidEmulatorInstance> devFrmInstances = devFramework.getAllInstances();
+ if (devFrmInstances != null)
+ {
+ for (IAndroidEmulatorInstance instance : devFrmInstances)
+ {
+ if (instance.isConnected())
+ {
+ connectedInstancesSet.add(instance);
+ }
+ }
+ }
+ }
+
+ return connectedInstancesSet;
+ }
+
+ /**
+ * Retrieves all started instances host addresses managed by every
+ * device framework which contributes with the deviceFramework extension point
+ *
+ * @return A collection containing all instances from all frameworks
+ */
+ public Set<String> getAllStartedInstancesHosts()
+ {
+ Set<String> hostSet = new HashSet<String>();
+ for (IDeviceFrameworkSupport devFramework : allFrameworks)
+ {
+ Collection<IAndroidEmulatorInstance> devFrmInstances = devFramework.getAllInstances();
+ if (devFrmInstances != null)
+ {
+ for (IAndroidEmulatorInstance instance : devFrmInstances)
+ {
+ if (instance.isStarted())
+ {
+ hostSet.add(instance.getInstanceIdentifier());
+ }
+ }
+ }
+ }
+
+ return hostSet;
+ }
+
+ /**
+ * Populates the allFrameworks collection with framework contributed
+ * classes for retrieving framework information.
+ */
+ private void populateModel()
+ {
+ IExtensionRegistry extReg = Platform.getExtensionRegistry();
+ IExtensionPoint extPoint = extReg.getExtensionPoint(DEV_FRAMEWORK_EXTENSION_POINT_ID);
+ IExtension[] extensions = extPoint.getExtensions();
+
+ for (IExtension aExtension : extensions)
+ {
+ IConfigurationElement[] configElements = aExtension.getConfigurationElements();
+ for (IConfigurationElement aConfig : configElements)
+ {
+ if (aConfig.getName().equals(DEV_FRAMEWORK_ELEM))
+ {
+ try
+ {
+ IDeviceFrameworkSupport devFramework =
+ (IDeviceFrameworkSupport) aConfig
+ .createExecutableExtension(DEV_FRAMEWORK_IMPL_CLASS_ATTR);
+ if (devFramework != null)
+ {
+ allFrameworks.add(devFramework);
+ }
+ }
+ catch (CoreException e)
+ {
+ // Do nothing.
+ // If a device framework cannot be instantiated, it will
+ // not be plugged to emulator core plugin.
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/devfrm/IDeviceFrameworkSupport.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/devfrm/IDeviceFrameworkSupport.java
new file mode 100644
index 0000000..427f106
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/devfrm/IDeviceFrameworkSupport.java
@@ -0,0 +1,46 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.devfrm;
+
+import java.util.Collection;
+
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+
+/**
+ * DESCRIPTION:
+ * Interface that must be implemented by every device framework
+ * that wishes to use the Android Emulator plug-ins
+ *
+ * RESPONSIBILITY:
+ * Provide every information that the Android Emulator plug-ins need
+ * to work with the registered framework
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * The class should be used by Eclipse only
+ */
+public interface IDeviceFrameworkSupport
+{
+ /**
+ * Retrieves a collection of the Android Emulator instances
+ * managed by this device framework
+ *
+ * @return Collection of the Android Emulator instances
+ */
+ Collection<IAndroidEmulatorInstance> getAllInstances();
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/AbstractEmuCtProvider.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/AbstractEmuCtProvider.java
new file mode 100644
index 0000000..8f9b094
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/AbstractEmuCtProvider.java
@@ -0,0 +1,248 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.emulationui;
+
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.ui.IViewSite;
+
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+
+/**
+ * DESCRIPTION:
+ * This class is the abstract parent of all emulation views content providers
+ *
+ * RESPONSIBILITY:
+ * Provide common method implementation for the several emulation views
+ * content providers
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * The class is used when a emulation view content provider is instantiated
+ */
+public abstract class AbstractEmuCtProvider implements ITreeContentProvider
+{
+ /**
+ * The id to be used when constructing a "sent to" node
+ */
+ public static final String SENT_TO_EMULATOR_ID = "sent_to";
+
+ /**
+ * The id to be used when constructing a "receive from" node
+ */
+ public static final String RECEIVE_FROM_EMULATOR_ID = "received_from";
+
+ /**
+ * The parent of the entire tree
+ */
+ private static IViewSite treeParent;
+
+ /**
+ * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(Object)
+ */
+ public Object getParent(Object element)
+ {
+
+ Object parent = null;
+
+ if (element instanceof EmuViewerNode)
+ {
+ EmuViewerNode nodeElement = (EmuViewerNode) element;
+
+ if (nodeElement instanceof EmuViewerRootNode)
+ {
+ // The IViewSite object is the parent of the whole tree
+ parent = treeParent;
+ }
+ else
+ {
+ parent = nodeElement.getParent();
+ }
+ }
+ else
+ {
+ warn("Tried to get parent of an object that is not an emulation tree node");
+ }
+
+ return parent;
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(Object)
+ */
+ public boolean hasChildren(Object object)
+ {
+
+ boolean hasChildren = false;
+
+ if (object instanceof EmuViewerNode)
+ {
+ EmuViewerNode nodeObject = (EmuViewerNode) object;
+
+ // The node has children if its children collection is bigger than 0 in size
+ hasChildren = (nodeObject.getChildren().size() > 0);
+
+ }
+ else
+ {
+ warn("Tried to test if an object that is not an emulation tree node has children");
+ }
+
+ return hasChildren;
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(Object)
+ */
+ public Object[] getElements(Object parent)
+ {
+
+ Object[] elements;
+
+ if (parent instanceof IViewSite)
+ {
+
+ if (treeParent == null)
+ {
+ // Sets the treeParent attribute to store which view site is the parent of the
+ // whole tree. This is done only once for each content provider instance.
+ // Each provider instance is supposed to be used with a single view
+ treeParent = (IViewSite) parent;
+ }
+
+ Collection<EmuViewerRootNode> emuNodeCollection = new HashSet<EmuViewerRootNode>();
+
+ Set<String> hostSet =
+ DeviceFrameworkManager.getInstance().getAllStartedInstancesHosts();
+
+ // A root node will be added per active emulator at the tree viewer
+ for (String host : hostSet)
+ {
+ EmuViewerRootNode node = new EmuViewerRootNode(host);
+ emuNodeCollection.add(node);
+
+ addChildrenToRootNode(node);
+ }
+
+ // Creating the array of elements (in this case, emulator root nodes) to be
+ // returned, when the parent is the view site itself
+ Object[] emuNodeArray = emuNodeCollection.toArray(new Object[emuNodeCollection.size()]);
+ elements = emuNodeArray;
+
+ }
+ else
+ {
+ // When elements different from the view site are provided, the elements will be
+ // their children
+ elements = getChildren(parent);
+ }
+
+ return elements;
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(Object)
+ */
+ public Object[] getChildren(Object parent)
+ {
+
+ Set<EmuViewerNode> childrenCollection;
+ Object[] returnArray;
+
+ if (parent instanceof EmuViewerNode)
+ {
+ // Firstly, try to retrieve the parent's children by means of the appropriate method
+ EmuViewerNode parentNode = (EmuViewerNode) parent;
+
+ childrenCollection = parentNode.getChildren();
+
+ // If the provided element is an emulator root node, it is needed to test if the
+ // intermediate nodes were already created (they are not created in the first request).
+ // If they were not created, assure that when the content framework requests, the
+ // intermediate nodes will be found.
+ //
+ // This procedure guarantees that once an emulator is started, it has the intermediate
+ // nodes constructed even if no emulation is being performed.
+ if (parentNode instanceof EmuViewerRootNode)
+ {
+ String host = ((EmuViewerRootNode) parentNode).getEmulatorIdentifier();
+ for (EmuViewerNode child : childrenCollection)
+ {
+ if (child.getChildren().size() == 0)
+ {
+
+ addChildrenToLeafParentNode(child, host);
+ }
+ }
+ }
+
+ // Creating the array of elements to be returned
+ returnArray = childrenCollection.toArray(new EmuViewerNode[childrenCollection.size()]);
+
+ }
+ else
+ {
+ warn("Tried to get children of an object that is not an emulation tree node");
+ returnArray = new Object[0];
+ }
+
+ return returnArray;
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.IContentProvider#dispose()
+ */
+ public void dispose()
+ {
+ // Do nothing
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(Viewer, Object, Object)
+ */
+ public void inputChanged(Viewer v, Object oldInput, Object newInput)
+ {
+ // Do nothing
+ }
+
+ /**
+ * Given a root node, adds children nodes to it
+ *
+ * @param root The root node that will receive the children nodes
+ */
+ protected void addChildrenToRootNode(EmuViewerRootNode root)
+ {
+
+ root.addChild(new EmuViewerNode(root, RECEIVE_FROM_EMULATOR_ID));
+ root.addChild(new EmuViewerNode(root, SENT_TO_EMULATOR_ID));
+ }
+
+ /**
+ * Given a node, adds children leaf nodes to it
+ *
+ * @param leafParentNode The node that will receive the children leaf nodes
+ * @param host The identifier of the emulator that owns this sub-tree
+ */
+ protected abstract void addChildrenToLeafParentNode(EmuViewerNode leafParentNode, String host);
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/AbstractEmuLabelProvider.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/AbstractEmuLabelProvider.java
new file mode 100644
index 0000000..812b40f
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/AbstractEmuLabelProvider.java
@@ -0,0 +1,286 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.emulationui;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ViewerCell;
+import org.eclipse.jface.viewers.ViewerRow;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+/**
+ * DESCRIPTION:
+ * This class contains the common logic for classes that retrieve the labels
+ * that are presented at the emulation views
+ *
+ * RESPONSIBILITY:
+ * Provide basic support to emulation label providers. This include
+ * - Maintaining the column index for determining which data is to be retrieved from the
+ * beans
+ * - Maintaining which is the column at the view left edge, to draw the tree correctly at
+ * that position
+ * - Updating the viewer cells in a standardized way, giving support to coloring a line
+ * in alternative color
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * The class is used when a emulation view content provider is instantiated
+ */
+public abstract class AbstractEmuLabelProvider extends ColumnLabelProvider
+{
+ /**
+ * The column that is being updated by this label provider
+ */
+ protected int columnIndex;
+
+ /**
+ * The index of the column that is currently in the left edge of the viewer
+ */
+ protected int firstColumnIndex;
+
+ /**
+ * The host that contains the bean that should be painted in an alternative color
+ * or <code>null</code> if no bean will have alternative color
+ */
+ private String alternativeColorHost;
+
+ /**
+ * The id that identifies the bean that should be painted in an alternative color
+ * or <code>null</code> if no bean will have alternative color
+ */
+ private long alternativeColorBeanId;
+
+ /**
+ * The color that is used to paint highlighted nodes
+ */
+ private final Color alternativeColor =
+ new Color(PlatformUI.getWorkbench().getDisplay(), 255, 255, 0);
+
+ /**
+ * Sets the index of the column that is currently at the left edge of the viewer
+ *
+ * @param firstColumnIndex The index of the column that is currently at the left
+ * edge of the viewer
+ */
+ public void setFirstColumnIndex(int firstColumnIndex)
+ {
+ this.firstColumnIndex = firstColumnIndex;
+ }
+
+ /**
+ * Retrieves the index of the column that is currently at the left edge of the viewer
+ *
+ * @return firstColumnIndex The index of the column that is currently at the left
+ * edge of the viewer
+ */
+ public int getFirstColumnIndex()
+ {
+ return firstColumnIndex;
+ }
+
+ /**
+ * Sets the host that contains the bean that should be painted in an alternative color
+ *
+ * @param host The host that contains the bean to be colored in alternative way, or
+ * <code>null</code> if no bean shall be colored in alternative way
+ */
+ public void setAlternativeColorHost(String host)
+ {
+ this.alternativeColorHost = host;
+ }
+
+ /**
+ * Sets the id that identifies the bean that should be painted in an alternative color
+ *
+ * @param beanId The id that identifies the bean that should be painted in an alternative
+ * color or <code>null</code> if no bean will have alternative color
+ */
+ public void setAlternativeColorBeanId(long beanId)
+ {
+ this.alternativeColorBeanId = beanId;
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.CellLabelProvider#update(org.eclipse.jface.viewers.ViewerCell)
+ */
+ @Override
+ public void update(ViewerCell cell)
+ {
+ // The instance column index is set with the current cell column index, as the logic
+ // contained in this class depends on this information. Then after the cell is
+ // updated according to the standard procedure, the column index field is reset so that
+ // it does not interfere with subsequent updates.
+ columnIndex = cell.getColumnIndex();
+ super.update(cell);
+ columnIndex = firstColumnIndex;
+
+ // Checks if the cell needs to be highlighted. This will be true if the values of
+ // alternativeColorHost and alternativeColorBeanId are different from null and -1
+ if ((alternativeColorHost != null) && (alternativeColorBeanId != -1))
+ {
+
+ Object element = cell.getElement();
+ // Only leaf nodes can be highlighted
+ if (element instanceof EmuViewerLeafNode)
+ {
+ // The next lines are used to check if the current element is the one to be
+ // highlighted. For that, the host and bean id needs to be compared to the
+ // alternativeColorHost and alternativeColorBeanId instance field values
+ EmuViewerLeafNode node = (EmuViewerLeafNode) element;
+ long beanId = node.getBeanId();
+ EmuViewerRootNode root = (EmuViewerRootNode) node.getParent().getParent();
+ String host = root.getEmulatorIdentifier();
+
+ if ((beanId == alternativeColorBeanId) && (host.equals(alternativeColorHost)))
+ {
+ // Highlighting the node
+
+ cell.setBackground(alternativeColor);
+
+ // Putting the node at the visible part of the tree
+
+ ViewerRow highlightedRow = cell.getViewerRow();
+ TreeItem highlightedItem = (TreeItem) highlightedRow.getItem();
+ Tree tree = (Tree) cell.getControl();
+ tree.showItem(highlightedItem);
+ }
+ }
+
+ }
+ }
+
+ /**
+ * @see org.eclipse.jface.viewers.ILabelProvider#getImage(Object)
+ */
+ @Override
+ public Image getImage(Object element)
+ {
+
+ Image imageToReturn = null;
+
+ // The image should appear near the label at the first cell in the row. That is why
+ // a test is being performed for the first column.
+ if ((element instanceof EmuViewerNode) && (isProvidingForFirstColumn()))
+ {
+ if (element instanceof EmuViewerRootNode)
+ {
+ // Get an common icon for emulator nodes
+
+ ImageDescriptor descriptor;
+ descriptor =
+ EmulatorPlugin.imageDescriptorFromPlugin(
+ EmulatorPlugin.PLUGIN_ID, IEmuIconPath.EMULATOR_ICON_PATH);
+ if (descriptor != null)
+ {
+ imageToReturn = descriptor.createImage();
+ }
+ }
+ else if (element instanceof EmuViewerLeafNode)
+ {
+ // Delegate the get method to the concrete class for the leaf node icon
+
+ imageToReturn = getLeafNodeIcon((EmuViewerLeafNode) element);
+ }
+ else
+ {
+ // Delegate the get method to the concrete class for the intermediate node icon
+
+ imageToReturn = getIntermediateNodeIcon((EmuViewerNode) element);
+ }
+ }
+
+ return imageToReturn;
+ }
+
+ /**
+ * Tests if the resource being retrieved (text or image) is for a cell at the first column
+ * of the tree viewer
+ *
+ * @return True if it is providing for the first column. False otherwise
+ */
+ protected boolean isProvidingForFirstColumn()
+ {
+ return firstColumnIndex == columnIndex;
+ }
+
+ /**
+ * Retrieves the icon that shall be displayed next to the provided node element. The
+ * provided node is one of the intermediate nodes in the tree, and does not represent
+ * neither the emulator itself nor the leaf element
+ *
+ * @param node The tree node that will have the returned icon by its side
+ *
+ * @return The icon that shall be displayed near the provided node
+ */
+ /**
+ * @see AbstractEmuLabelProvider#getIntermediateNodeIcon(EmuViewerNode)
+ */
+ protected Image getIntermediateNodeIcon(EmuViewerNode node)
+ {
+
+ Image imageToReturn = null;
+ ImageDescriptor descriptor;
+
+ if (node.getNodeId().equals(AbstractEmuCtProvider.SENT_TO_EMULATOR_ID))
+ {
+ descriptor =
+ EmulatorPlugin.imageDescriptorFromPlugin(EmulatorPlugin.PLUGIN_ID,
+ IEmuIconPath.SENT_TO_ICON_PATH);
+ }
+ else
+ {
+ descriptor =
+ EmulatorPlugin.imageDescriptorFromPlugin(EmulatorPlugin.PLUGIN_ID,
+ IEmuIconPath.RECEIVE_FROM_ICON_PATH);
+ }
+
+ if (descriptor != null)
+ {
+ imageToReturn = descriptor.createImage();
+ }
+
+ return imageToReturn;
+ }
+
+ /**
+ * Retrieves the icon that shall be displayed next to the provided leaf node element
+ *
+ * @param node The tree node that will have the returned icon by its side
+ *
+ * @return The icon that shall be displayed near the provided node
+ */
+ protected abstract Image getLeafNodeIcon(EmuViewerLeafNode node);
+
+ /**
+ * Gets the text referring to a particular leaf node, at the provided column.
+ *
+ * @param element The tree node that identifies the bean that needs to have information
+ * retrieved from
+ * @param columnIndex The id of the column that identifies which information to take from the
+ * bean
+ *
+ * @return The text from the bean at the specified column
+ */
+ public abstract String getText(EmuViewerLeafNode node, int columnIndex);
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerLeafNode.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerLeafNode.java
new file mode 100644
index 0000000..4f1da74
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerLeafNode.java
@@ -0,0 +1,71 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.emulationui;
+
+/**
+ * DESCRIPTION:
+ * This is a utility class used to represent a leaf node of the emulation views
+ * tree viewer
+ *
+ * RESPONSIBILITY:
+ * Guarantee that no children is added to this leaf node
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * A class should construct an instance of this class whenever it wishes
+ * to add a leaf node to an emulation view tree
+ */
+public abstract class EmuViewerLeafNode extends EmuViewerNode
+{
+ /**
+ * Constructor
+ *
+ * @see EmuViewerNode#EmuViewerNode(EmuViewerNode, String)
+ */
+ public EmuViewerLeafNode(EmuViewerNode parent, String nodeId)
+ {
+ super(parent, nodeId);
+ }
+
+ /**
+ * @see EmuViewerNode#addChild(EmuViewerNode)
+ */
+ @Override
+ public void addChild(EmuViewerNode child)
+ {
+ // Do nothing
+ }
+
+ /**
+ * Retrieves the id that identifies the bean that provides data to this node
+ *
+ * @return The bean identifier
+ */
+ public abstract long getBeanId();
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ // For leaf nodes, identify which is the concrete class name and append to that name
+ // the associated bean id
+ return getClass().getSimpleName() + ":" + getBeanId();
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerNode.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerNode.java
new file mode 100644
index 0000000..f452760
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerNode.java
@@ -0,0 +1,152 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.emulationui;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * DESCRIPTION:
+ * This class represents a node in the tree presented in a emulation view
+ *
+ * RESPONSIBILITY:
+ * Guarantee the tree structure by maintaining the parent/child relationship
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * A class should construct an instance of this class whenever it wishes
+ * to add a node to an emulation view tree
+ */
+public class EmuViewerNode
+{
+ /**
+ * The parent node of this node
+ */
+ private final EmuViewerNode parent;
+
+ /**
+ * An id that identifies the node type.
+ * The id meaning is defined by the user
+ */
+ private final String nodeId;
+
+ /**
+ * The error message to use as the node label
+ * If <code>null</code>, use regular label resolution
+ */
+ private String errorMessage = null;
+
+ /**
+ * A set containing all children of this node
+ */
+ private final Set<EmuViewerNode> children = new HashSet<EmuViewerNode>();
+
+ /**
+ * Constructor.
+ *
+ * @param parent The parent node of this node
+ * @param nodeId An id that identifies the node type
+ */
+ public EmuViewerNode(EmuViewerNode parent, String nodeId)
+ {
+ this.parent = parent;
+ this.nodeId = nodeId;
+ }
+
+ /**
+ * Retrieves the node's parent
+ *
+ * @return The parent node
+ */
+ public EmuViewerNode getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * Retrieves the id of this node. The id meaning is defined by the class user
+ *
+ * @return The id of this node
+ */
+ public String getNodeId()
+ {
+ return nodeId;
+ }
+
+ /**
+ * Adds a new child to this node
+ *
+ * @param child The child to be added to the node
+ */
+ public void addChild(EmuViewerNode child)
+ {
+ children.add(child);
+ }
+
+ /**
+ * Retrieves all this node's children
+ *
+ * @return A set containing all children of this node
+ */
+ public Set<EmuViewerNode> getChildren()
+ {
+ return children;
+ }
+
+ /**
+ * Sets an error message to display as the node label.
+ * If <code>null</code>, the regular label resolution is used
+ *
+ * @param errorMessage An error message to display or <code>null</code> if it is
+ * desired to have regular label resolution for this node
+ */
+ public void setErrorMessage(String errorMessage)
+ {
+ this.errorMessage = errorMessage;
+ }
+
+ /**
+ * Tests if this node has an error message assigned
+ *
+ * @return True if an error message was assigned; false otherwise
+ */
+ public boolean hasErrorMessage()
+ {
+ return errorMessage != null;
+ }
+
+ /**
+ * Retrieves the error message assigned to this node
+ *
+ * @return The error message, or <code>null</code> if no error message was assigned
+ */
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ // For generic/intermediate nodes use the node id itself
+ return getNodeId();
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerRootNode.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerRootNode.java
new file mode 100644
index 0000000..0188835
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/EmuViewerRootNode.java
@@ -0,0 +1,94 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.emulationui;
+
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import com.motorola.studio.android.emulator.core.exception.InstanceNotFoundException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.utils.EmulatorCoreUtils;
+
+/**
+ * DESCRIPTION:
+ * This class represents the parent of all nodes in the tree presented in a emulation view
+ * It must have reference to the emulator host, so that the tree is separated in
+ * several emulator sub-trees
+ *
+ * RESPONSIBILITY:
+ * To be the root of the emulator tree and maintain information about which emulator is
+ * owner of the sub-tree that has this node as root
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * A class should construct an instance of this class whenever an emulator information is to be
+ * included at an emulation view
+ */
+public class EmuViewerRootNode extends EmuViewerNode
+{
+ /**
+ * The emulator identifier (serial port number)
+ */
+ private final String serial;
+
+ /**
+ * Constructor.
+ *
+ * @param identifier The identifier of the emulator that owns the sub-tree starting at this node
+ */
+ public EmuViewerRootNode(String identifier)
+ {
+ super(null, "ROOT");
+ this.serial = identifier;
+ }
+
+ /**
+ * Gets the host of the emulator that owns the sub-tree starting at this node
+ *
+ * @return The emulator host
+ */
+ public String getEmulatorIdentifier()
+ {
+ return serial;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ String classString;
+
+ // For emulator root nodes, the toString method should provide the emulator instance
+ // name. If it is not possible to retrieve the instance name, print the host itself
+ String serial = getEmulatorIdentifier();
+ try
+ {
+ IAndroidEmulatorInstance instance =
+ EmulatorCoreUtils.getAndroidInstanceByIdentifier(serial);
+ classString = instance.getName();
+ }
+ catch (InstanceNotFoundException e)
+ {
+ warn("The instance could not be found for retrieving its name. Using serial port instead.");
+ classString = serial;
+ }
+
+ return classString;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/IEmuIconPath.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/IEmuIconPath.java
new file mode 100644
index 0000000..164f403
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/IEmuIconPath.java
@@ -0,0 +1,30 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.emulationui;
+
+/**
+ * This interface contains the paths to the icons that are used by the emulation views
+ */
+public interface IEmuIconPath
+{
+ // Emulation root node icon
+ String EMULATOR_ICON_PATH = "resource/emulator.png";
+
+ // Emulation intermediate nodes icons
+ String SENT_TO_ICON_PATH = "resource/sentbyemulator.png";
+
+ String RECEIVE_FROM_ICON_PATH = "resource/receivebyemulator.png";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/SrcDestComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/SrcDestComposite.java
new file mode 100644
index 0000000..a70194e
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/emulationui/SrcDestComposite.java
@@ -0,0 +1,439 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.emulationui;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.FontMetrics;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * DESCRIPTION:
+ * This is a composite that is used by several emulation UI elements to choose source and
+ * destination elements
+ *
+ * RESPONSIBILITY:
+ * Provide means for the user to choose which emulator and phone number will be involved
+ * in a emulation
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Add the composite to a UI element that needs to have a emulator and a phone number
+ * chosen by the user
+ */
+public class SrcDestComposite extends Composite
+{
+ /**
+ * Message that will be shown near the emulator combo
+ */
+ private String emulatorLabelStr;
+
+ /**
+ * Message that will be shown near the phone number text field
+ */
+ private String phoneNumberLabelStr;
+
+ /**
+ * Emulator currently selected
+ */
+ private String selectedEmulator;
+
+ /**
+ * Phone number currently selected
+ */
+ private String selectedPhoneNumber;
+
+ /**
+ * True if the composite is valid and can provide information to the user class
+ * False if not.
+ */
+ private boolean isValid = false;
+
+ /**
+ * Error message to be shown to the user if the composite data is not valid
+ */
+ private String errorMessage =
+ NLS.bind(EmulatorNLS.ERR_SrcDestComposite_InvalidFillingBase,
+ EmulatorNLS.ERR_SrcDestComposite_InvalidFillingPhoneNumber,
+ EmulatorNLS.ERR_SrcDestComposite_InvalidFillingEmulator);
+
+ // Widgets
+ private Combo runningEmulatorsCombo;
+
+ private Text phoneNumberText;
+
+ // attribute for calculating label sizes (for layout purposes)
+ private FontMetrics fontMetrics = null;
+
+ /**
+ * Constructor.
+ *
+ * @param parent The parent composite of this one
+ * @param style Style of the composite. See constants at SWT class
+ * @param showSrcControls True if this composite should show
+ * the emulation source controls. False otherwise
+ * @param isEmulatorSrc True if this composite will have the emulator
+ * part as source in emulation. False if the phone number
+ * will be the source
+ */
+ public SrcDestComposite(Composite parent, int style, boolean showSrcControls,
+ boolean isEmulatorSrc)
+ {
+ super(parent, style);
+
+ GridLayout layout = new GridLayout(2, false);
+ layout.marginHeight = 5;
+ layout.marginWidth = 5;
+ layout.verticalSpacing = 5;
+ layout.horizontalSpacing = 2;
+ this.setLayout(layout);
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ this.setLayoutData(data);
+
+ // initialize font metrics
+ GC gc = new GC(this);
+ gc.setFont(this.getFont());
+ fontMetrics = gc.getFontMetrics();
+ gc.dispose();
+
+ if (isEmulatorSrc)
+ {
+ // When emulator is the source part, its UI is build prior to phone number UI,
+ // and appropriate labels are used for both
+ debug("Using emulator as source");
+ emulatorLabelStr = EmulatorNLS.UI_SrcDestComposite_OriginatingRunningEmulatorLabel;
+ phoneNumberLabelStr = EmulatorNLS.UI_SrcDestComposite_DestinationPhoneNumberLabel;
+ if (showSrcControls)
+ {
+ debug("Showing source controls");
+ createEmulatorUI();
+ }
+ createPhoneNumberUI();
+ }
+ else
+ {
+ // When phone number is the source part, its UI is build prior to emulator UI,
+ // and appropriate labels are used for both
+ debug("Using phone number as source");
+ emulatorLabelStr = EmulatorNLS.UI_SrcDestComposite_DestinationRunningEmulatorLabel;
+ phoneNumberLabelStr = EmulatorNLS.UI_SrcDestComposite_OriginatingPhoneNumberLabel;
+ if (showSrcControls)
+ {
+ debug("Showing source controls");
+ createPhoneNumberUI();
+ }
+ createEmulatorUI();
+ }
+
+ addListeners();
+
+ // call the check method to refresh error message.
+ checkData();
+
+ }
+
+ /**
+ * Build the emulator part controls
+ */
+ private void createEmulatorUI()
+ {
+
+ Label runningEmulatorsLabel = new Label(this, SWT.NONE);
+ runningEmulatorsLabel.setText(emulatorLabelStr);
+ GridData data = new GridData(SWT.FILL, SWT.CENTER, false, false);
+ data.widthHint = getLabelWidthHint(runningEmulatorsLabel);
+ runningEmulatorsLabel.setLayoutData(data);
+
+ this.runningEmulatorsCombo = new Combo(this, SWT.BORDER | SWT.READ_ONLY);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false);
+ this.runningEmulatorsCombo.setLayoutData(data);
+ populateEmulatorCombo();
+
+ }
+
+ /**
+ * Build the phone number part controls
+ */
+ private void createPhoneNumberUI()
+ {
+
+ Label phoneNumberLabel = new Label(this, SWT.NONE);
+ phoneNumberLabel.setText(phoneNumberLabelStr);
+ GridData data = new GridData(SWT.FILL, SWT.CENTER, false, false);
+ data.widthHint = getLabelWidthHint(phoneNumberLabel);
+ phoneNumberLabel.setLayoutData(data);
+
+ this.phoneNumberText = new Text(this, SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false);
+ this.phoneNumberText.setLayoutData(data);
+ this.phoneNumberText.setTextLimit(40);
+
+ }
+
+ /**
+ * Add listeners to the composite controls
+ */
+ private void addListeners()
+ {
+
+ if (runningEmulatorsCombo != null)
+ {
+ runningEmulatorsCombo.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ selectedEmulator = getCurrentlySelectedIdentifier();
+ checkData();
+ }
+ });
+ }
+
+ if (phoneNumberText != null)
+ {
+ phoneNumberText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ selectedPhoneNumber = phoneNumberText.getText();
+ checkData();
+ }
+ });
+ }
+ }
+
+ /**
+ * Defines the width hint to be used for the given label on a GridData object.
+ *
+ * @param label the label to calculate the width hint for
+ *
+ * @return the width hint
+ */
+ private int getLabelWidthHint(Label label)
+ {
+ int widthHint = Dialog.convertHorizontalDLUsToPixels(fontMetrics, label.getText().length());
+ return Math.max(widthHint, label.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
+ }
+
+ /**
+ * Populates the emulator combo box with the currently running emulators
+ */
+ private void populateEmulatorCombo()
+ {
+
+ // Populating emulator combo with all running emulator names
+ // Besides, keeping an array of the identifiers as combo data.
+ Map<String, String> identifiersAndNames = new HashMap<String, String>();
+ Collection<IAndroidEmulatorInstance> startedInstances =
+ DeviceFrameworkManager.getInstance().getAllStartedInstances();
+ for (IAndroidEmulatorInstance instance : startedInstances)
+ {
+ identifiersAndNames.put(instance.getInstanceIdentifier(), instance.getName());
+ }
+
+ String[] instanceNamesArray = new String[identifiersAndNames.size()];
+ String[] identifiersArray = new String[identifiersAndNames.size()];
+ int i = 0;
+
+ Set<String> identifiers = identifiersAndNames.keySet();
+ for (String identifier : identifiers)
+ {
+
+ String viewerName = identifiersAndNames.get(identifier);
+
+ // It is VERY important that the index used at the data array is equal to the
+ // index used at the items array. According to the selected item in the combo, the
+ // corresponding identifier is retrieved from the data array in the future
+ instanceNamesArray[i] = viewerName;
+ identifiersArray[i] = identifier;
+ i++;
+ }
+
+ runningEmulatorsCombo.setItems(instanceNamesArray);
+ runningEmulatorsCombo.setData(identifiersArray);
+
+ // if there is just one emulator in the combo list,
+ // it will be chose by default.
+ if (runningEmulatorsCombo.getItemCount() == 1)
+ {
+ runningEmulatorsCombo.select(0);
+ selectedEmulator = getCurrentlySelectedIdentifier();
+ checkData();
+ }
+
+ }
+
+ /**
+ * Retrieve the identifier of the selected instance at Android Emulator combo box
+ *
+ * @return The identifier, or an empty string if no emulator is selected
+ */
+ private String getCurrentlySelectedIdentifier()
+ {
+
+ String currentlySelectedSerial = "";
+ int index = runningEmulatorsCombo.getSelectionIndex();
+
+ if (index >= 0)
+ {
+ String[] serials = (String[]) runningEmulatorsCombo.getData();
+ currentlySelectedSerial = serials[index];
+
+ }
+
+ return currentlySelectedSerial;
+ }
+
+ /**
+ * Get the emulator identifier that was selected by the user
+ *
+ * @return The selected emulator identifier
+ */
+ public String getSelectedEmulator()
+ {
+ return selectedEmulator;
+ }
+
+ /**
+ * Get the phone number that was typed by the user
+ *
+ * @return The phone number typed by the user
+ */
+ public String getSelectedPhoneNumber()
+ {
+ return selectedPhoneNumber;
+ }
+
+ /**
+ * Tests if the values chosen/typed by the user are valid
+ * By invoking this method, the user class is able to know if it can proceed
+ *
+ * @return True if the user has chosen valid values. False otherwise
+ */
+ public boolean isValid()
+ {
+ return isValid;
+ }
+
+ /**
+ * Retrieves the error message to be shown to the user if the composite is
+ * not valid
+ *
+ * @return The error message if the composite is not valid, or <code>null</code> if
+ * the composite is valid and no error message should be displayed.
+ */
+ public String getErrorMessage()
+ {
+ return errorMessage;
+ }
+
+ /**
+ * Check if the data entered by the user is correct and set instance variables
+ * to store the test results
+ */
+ private void checkData()
+ {
+
+ isValid = false;
+
+ boolean isEmulatorValid = false;
+ boolean isPhoneNumberValid = false;
+
+ boolean isUsingPhoneNumber = (phoneNumberText != null);
+ boolean isUsingEmulator = (runningEmulatorsCombo != null);
+
+ // Tests if emulator selection is valid.
+ //
+ // If the emulator combo is null, that means that the user decided not to use it. In
+ // this case, it will always be valid. Otherwise, the combo selection needs to be
+ // not null and not blank
+ if ((!isUsingEmulator) || ((selectedEmulator != null) && (!selectedEmulator.equals(""))))
+ {
+ isEmulatorValid = true;
+ }
+
+ // Tests if phone number selection is valid.
+ //
+ // If the phone number text is null, that means that the user decided not to use it. In
+ // this case, it will always be valid. Otherwise, the text field selection needs to be
+ // not null, not blank and can be parsed to double (that means that the contents are
+ // composed by numerals only)
+ if (!isUsingPhoneNumber)
+ {
+ isPhoneNumberValid = true;
+ }
+ else if ((selectedPhoneNumber != null) && (!selectedPhoneNumber.equals("")))
+ {
+ Pattern p = Pattern.compile("(\\d)+");
+ Matcher m = p.matcher(selectedPhoneNumber);
+ isPhoneNumberValid = m.matches();
+ }
+
+ // Based on previous checks, determine if the composite state is valid
+ if (isEmulatorValid && isPhoneNumberValid)
+ {
+ isValid = true;
+ errorMessage = null;
+ }
+ else
+ {
+ // If not valid, an error message will be shown. The following calculations
+ // are for determining which error has happened to build the message
+ String phoneNumberError = "";
+ String emulatorError = "";
+
+ if (isUsingPhoneNumber && (!isPhoneNumberValid))
+ {
+ phoneNumberError = EmulatorNLS.ERR_SrcDestComposite_InvalidFillingPhoneNumber;
+ }
+ if (isUsingEmulator && (!isEmulatorValid))
+ {
+ emulatorError = EmulatorNLS.ERR_SrcDestComposite_InvalidFillingEmulator;
+ }
+
+ errorMessage =
+ NLS.bind(EmulatorNLS.ERR_SrcDestComposite_InvalidFillingBase,
+ phoneNumberError, emulatorError);
+ }
+
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceNotFoundException.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceNotFoundException.java
new file mode 100644
index 0000000..c58a9a3
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceNotFoundException.java
@@ -0,0 +1,58 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.exception;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+/**
+ * Represents an exception thrown every time a Android Emulator instance is not found
+ * at the device framework.
+ */
+@SuppressWarnings("serial")
+public class InstanceNotFoundException extends AndroidException
+{
+ /**
+ * Creates a new InstanceNotFoundException object.
+ */
+ public InstanceNotFoundException()
+ {
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ */
+ public InstanceNotFoundException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * @param cause the associated cause.
+ */
+ public InstanceNotFoundException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ * @param cause the associated cause.
+ */
+ public InstanceNotFoundException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceStartException.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceStartException.java
new file mode 100644
index 0000000..2e328cf
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceStartException.java
@@ -0,0 +1,57 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.exception;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+/**
+ * Represents an exception thrown if the emulator instance cannot be started
+ */
+@SuppressWarnings("serial")
+public class InstanceStartException extends AndroidException
+{
+ /**
+ * Creates a new InstanceStartException object.
+ */
+ public InstanceStartException()
+ {
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ */
+ public InstanceStartException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * @param cause the associated cause.
+ */
+ public InstanceStartException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ * @param cause the associated cause.
+ */
+ public InstanceStartException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceStopException.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceStopException.java
new file mode 100644
index 0000000..beadb1d
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/InstanceStopException.java
@@ -0,0 +1,57 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.exception;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+/**
+ * Represents an exception thrown if the emulator instance cannot be stopped
+ */
+@SuppressWarnings("serial")
+public class InstanceStopException extends AndroidException
+{
+ /**
+ * Creates a new InstanceStopException object.
+ */
+ public InstanceStopException()
+ {
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ */
+ public InstanceStopException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * @param cause the associated cause.
+ */
+ public InstanceStopException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ * @param cause the associated cause.
+ */
+ public InstanceStopException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/SkinException.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/SkinException.java
new file mode 100644
index 0000000..79c96cd
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/SkinException.java
@@ -0,0 +1,58 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.exception;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+/**
+ * Represents an exception thrown every time the skin construction
+ * results in error.
+ */
+@SuppressWarnings("serial")
+public class SkinException extends AndroidException
+{
+ /**
+ * Creates a new SkinException object.
+ */
+ public SkinException()
+ {
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ */
+ public SkinException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * @param cause the associated cause.
+ */
+ public SkinException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ * @param cause the associated cause.
+ */
+ public SkinException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/StartCancelledException.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/StartCancelledException.java
new file mode 100644
index 0000000..e9d8105
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/StartCancelledException.java
@@ -0,0 +1,58 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.exception;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+/**
+ * Represents the exception thrown when the user cancels the emulator
+ * start process
+ */
+@SuppressWarnings("serial")
+public class StartCancelledException extends AndroidException
+{
+ /**
+ * Creates a new StartCancelledException object.
+ */
+ public StartCancelledException()
+ {
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ */
+ public StartCancelledException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * @param cause the associated cause.
+ */
+ public StartCancelledException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ * @param cause the associated cause.
+ */
+ public StartCancelledException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/StartTimeoutException.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/StartTimeoutException.java
new file mode 100644
index 0000000..dba69f0
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/exception/StartTimeoutException.java
@@ -0,0 +1,58 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.exception;
+
+import com.motorola.studio.android.common.exception.AndroidException;
+
+/**
+ * Represents the exception thrown when the emulator start process cannot end
+ * within the set timeout
+ */
+@SuppressWarnings("serial")
+public class StartTimeoutException extends AndroidException
+{
+ /**
+ * Creates a new StartTimeoutException object.
+ */
+ public StartTimeoutException()
+ {
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ */
+ public StartTimeoutException(String message)
+ {
+ super(message);
+ }
+
+ /**
+ * @param cause the associated cause.
+ */
+ public StartTimeoutException(Throwable cause)
+ {
+ super(cause);
+ }
+
+ /**
+ * @param message the message used by the Exception.
+ * @param cause the associated cause.
+ */
+ public StartTimeoutException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/AbstractInputLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/AbstractInputLogic.java
new file mode 100644
index 0000000..cb3cd60
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/AbstractInputLogic.java
@@ -0,0 +1,55 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.model;
+
+/**
+ * Basic implementation of the IInputLogic interface.
+ * Provides common methods for all types of input
+ *
+ */
+public abstract class AbstractInputLogic implements IInputLogic
+{
+ /**
+ * The instance associated to this input
+ */
+ private IAndroidEmulatorInstance instance;
+
+ /**
+ * Retrieves the instance associated with this input
+ * @return
+ */
+ public IAndroidEmulatorInstance getInstance()
+ {
+ return instance;
+ }
+
+ /**
+ * Executes whatever is necessary before sending keys
+ */
+ public void init(IAndroidEmulatorInstance instance)
+ {
+ this.instance = instance;
+ }
+
+ /**
+ * Executes whatever is necessary before disposing the object
+ */
+ public void dispose()
+ {
+ //do nothing
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IAndroidEmulatorInstance.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IAndroidEmulatorInstance.java
new file mode 100644
index 0000000..1542798
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IAndroidEmulatorInstance.java
@@ -0,0 +1,228 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.model;
+
+import java.io.File;
+import java.util.Properties;
+
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+
+/**
+ * DESCRIPTION:
+ * This class represents the Android Emulator instance contract.
+ *
+ * RESPONSIBILITY:
+ * Define which information is required from a device that wishes to use
+ * the Android Emulator viewer.
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use the methods to retrieve information from an Android Emulator device instance
+ */
+public interface IAndroidEmulatorInstance
+{
+ /**
+ * Gets the instance name in simplified format
+ *
+ * @return The instance name
+ */
+ String getName();
+
+ /**
+ * Gets the instance name in full format
+ *
+ * @return The instance name
+ */
+ String getFullName();
+
+ /**
+ * Gets the identifier for this instance, that is available when the emulator is started.
+ *
+ * @return the instance identifier
+ */
+ String getInstanceIdentifier();
+
+ /**
+ * Sets the current layout being used by this instance
+ *
+ * @param layoutName The new layout
+ */
+ void setCurrentLayout(String layoutName);
+
+ /**
+ * Gets the current known status of the flip or slide
+ *
+ * @return True if the flip/slide is closed; false otherwise
+ */
+ String getCurrentLayout();
+
+ /**
+ * Sets the parameter used to determine if this instance has CLI display or not
+ *
+ * @param hasCli True if the instance has CLI; false otherwise
+ */
+ void setHasCli(boolean hasCli);
+
+ /**
+ * Gets the parameter used to determine if this instance has CLI display or not
+ *
+ * @return True if the instance has CLI; false otherwise
+ */
+ boolean getHasCli();
+
+ /**
+ * Sets the Android protocol object created when connecting to the VM
+ *
+ * @param handle The Android protocol object provided when connecting
+ * the protocol
+ */
+ void setProtocolHandle(ProtocolHandle handle);
+
+ /**
+ * Gets the Android protocol object object that identifies the protocol connection
+ *
+ * @returns The Android protocol object representing the connection of this
+ * instance
+ */
+ ProtocolHandle getProtocolHandle();
+
+ /**
+ * Gets the id of the skin logic being used for this instance
+ *
+ * @return The skin id
+ */
+ String getSkinId();
+
+ /**
+ * Gets the path of the files being used to draw the skin for this instance
+ *
+ * @return A pointer to the folder that contains the files to be used
+ * to draw the skin for this instance
+ */
+ File getSkinPath();
+
+ /**
+ * Tests if the instance is started
+ *
+ * @return True if it is started; false otherwise
+ */
+ boolean isStarted();
+
+ /**
+ * Test if the instance is connected, i.e.
+ * The communication protocol is running
+ *
+ * @return True if it is connected; false otherwise
+ */
+ boolean isConnected();
+
+ /**
+ * Test if the instance is available, i.e.
+ * The instance type is available for the current SDK setup.
+ *
+ * @return True if it's available; false otherwise
+ */
+ boolean isAvailable();
+
+ /**
+ * Stops the Android Emulator instance
+ *
+ * @param force whether the stop should be forced or not
+ *
+ * @throws InstanceStopException If the instance fails to stop
+ */
+ void stop(boolean force) throws InstanceStopException;
+
+ /**
+ * Retrieves the collection of all instance properties
+ *
+ * @return The collection of instance properties
+ */
+ public Properties getProperties();
+
+ /**
+ * Retrieves the input logic used by this instance to send data to the emulator
+ *
+ * @return The input logic used by this instance
+ */
+ public IInputLogic getInputLogic();
+
+ /**
+ * Gets the emulator process associated to this instance when it's running
+ * @return the Process representing the emulator process
+ */
+ public Process getProcess();
+
+ /**
+ * Sets the emulator process associated to this emulator instance while it's running
+ * @param process
+ */
+ public void setProcess(Process process);
+
+ /**
+ * Performs any needed operations to change the instance orientation/rotation
+ *
+ * @param args Additional data provided by the skin to perform the operation
+ */
+ public void changeOrientation(String args);
+
+ /**
+ * Get the Android target that the instance is compliant to
+ *
+ * @return Android target that the instance is compliant to
+ */
+ public String getTarget();
+
+ /**
+ * Get the Android API level that the instance is compliant to
+ *
+ * @return Android API level that the instance is compliant to
+ */
+ public int getAPILevel();
+
+ /**
+ * Get the Android Emulator window handle
+ *
+ * @return Android target that the instance is compliant to
+ */
+ public long getWindowHandle();
+
+ /**
+ * Sets the handle of the emulator window associated with the instance
+ *
+ * @param handle
+ */
+ public void setWindowHandle(long handle);
+
+ /**
+ *
+ * @param contentComposite
+ */
+ public void setComposite(Composite composite);
+
+ /**
+ *
+ * @return
+ */
+ public Composite getComposite();
+
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IEmulatorView.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IEmulatorView.java
new file mode 100644
index 0000000..b87efa6
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IEmulatorView.java
@@ -0,0 +1,36 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.model;
+
+/**
+ * DESCRIPTION:
+ * Defines the method that every Android Emulator view should have
+ * for self-updating
+ *
+ * RESPONSIBILITY:
+ * Define the method that allows the Android Emulator views to self
+ * update
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Call the interface method for view refreshing.
+ */
+public interface IEmulatorView
+{
+ void refreshView();
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IInputLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IInputLogic.java
new file mode 100644
index 0000000..2cb9c67
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/model/IInputLogic.java
@@ -0,0 +1,56 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.model;
+
+import java.util.Properties;
+
+public interface IInputLogic
+{
+ void init(IAndroidEmulatorInstance instance);
+
+ void dispose();
+
+ /**
+ * Send a key press event
+ *
+ * @param character the correspondent character
+ * @param keycode the keycode of the key pressed
+ * @param keyCodeMap the skin keycode map
+ */
+ void sendKey(int character, int keycode, Properties keyCodeMap);
+
+ /**
+ * Send a click event
+ *
+ * @param code the code to be sent
+ * @param pressed key pressed - yes or no
+ */
+ void sendClick(int code, boolean pressed);
+
+ /**
+ * Send a click event
+ *
+ * @param code the code to be sent
+ * @param pressed key pressed - yes or no
+ */
+ void sendClick(String code, boolean pressed);
+
+ void sendMouseUp(int x, int y);
+
+ void sendMouseDown(int x, int y);
+
+ void sendMouseMove(int x, int y);
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/AndroidPressKey.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/AndroidPressKey.java
new file mode 100644
index 0000000..e66bae0
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/AndroidPressKey.java
@@ -0,0 +1,251 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.skin;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.StringTokenizer;
+
+import org.eclipse.swt.graphics.Rectangle;
+
+/**
+ * DESCRIPTION:
+ * This is a default implementation of the interface IAndroidEmulatorKey
+ *
+ * RESPONSIBILITY:
+ * - Provide an easy way to find the keys pressed during mouse interaction
+ * at skin
+ * - Provide means of retrieving the keysym associated with each key
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Provide a coordinate to the isInsideKey methods to test if the coordinate
+ * is inside the key area
+ * Use the getKeysym of a key to retrieve the code that needs to be sent in a
+ * key event message to the server, informing that a key was pressed or released
+ */
+public class AndroidPressKey implements IAndroidKey
+{
+ // Constants used in the isFlipValid method
+ private static final int FLIP_SLIDE_OPENED_ONLY = 0;
+
+ private static final int FLIP_SLIDE_CLOSED_ONLY = 1;
+
+ private static final int FLIP_SLIDE_OPENED_AND_CLOSED = 2;
+
+ /**
+ * Fields that can be found in an ordinary key.xml file
+ */
+ private final String name;
+
+ private final String toolTip;
+
+ private final String keycode;
+
+
+
+ private final Rectangle keyArea;
+
+ private final int flipSlideEnabledCode;
+
+ private final Collection<String> morphingModeCollection = new LinkedHashSet<String>();
+
+ /**
+ * Creates a new AndroidPressKey object.
+ *
+ * @param name The key name. This is usually a human readable skin, that
+ * provides key identification
+ * @param keycode The code that will be sent to server if the key is pressed
+ * @param toolTip The text that will be shown as the key tool tip.
+ * @param startx X coordinate of the upper left corner of the key
+ * @param starty Y coordinate of the upper left corner of the key
+ * @param endx X coordinate of the lower right corner of the key
+ * @param endy Y coordinate of the lower right corner of the key
+ * @param morphingModes A comma separated list of morphing modes to which this
+ * key applies, or null if not applicable
+ * @param flipenabled true if this key is valid in closed flip mode;
+ * false if the key is valid in opened flip mode
+ */
+ public AndroidPressKey(String name, String keycode, String toolTip, int startx, int starty,
+ int endx, int endy, String morphingModes, int flipEnabledCode)
+ {
+ this(name, keycode, toolTip, new Rectangle(startx, starty, endx - startx, endy - starty),
+ morphingModes, flipEnabledCode);
+ }
+
+ /**
+ * Creates a new AndroidPressKey object.
+ *
+ * @param name The key name. This is usually a human readable skin, that
+ * provides key identification
+ * @param keycode The code that will be sent to server if the key is pressed
+ * @param toolTip The text that will be shown as the key tool tip.
+ * @param key A rectangle that represents the key area at skin
+ * @param morphingModes A comma separated list of morphing modes to which this
+ * key applies, or null if not applicable
+ * @param flipenabled true if this key is valid in closed flip mode;
+ * false if the key is valid in opened flip mode
+ */
+ public AndroidPressKey(String name, String keycode, String toolTip, Rectangle key,
+ String morphingModes, int flipEnabledCode)
+ {
+ this.name = name;
+ this.keycode = keycode;
+ this.toolTip = toolTip;
+ this.keyArea = key;
+ this.flipSlideEnabledCode = flipEnabledCode;
+
+ if (morphingModes != null)
+ {
+ StringTokenizer st = new StringTokenizer(morphingModes, ",");
+ String token;
+
+ while (st.hasMoreTokens())
+ {
+ token = st.nextToken();
+ morphingModeCollection.add(token);
+ }
+ }
+ }
+
+ /**
+ * @see IAndroidKey#getKeysym()
+ */
+ public String getKeysym()
+ {
+ return keycode;
+ }
+
+ /**
+ * @see IAndroidKey#isInsideKey(int, int)
+ */
+ public boolean isInsideKey(int x, int y)
+ {
+ return keyArea.contains(x, y);
+ }
+
+ /**
+ * Retrieves the X coordinate of the lower right corner of the key
+ *
+ * @return X coordinate of the lower right corner of the key
+ */
+ public int getEndx()
+ {
+ return keyArea.x + keyArea.width;
+ }
+
+ /**
+ * Retrieves the Y coordinate of the lower right corner of the key
+ *
+ * @return Y coordinate of the lower right corner of the key
+ */
+ public int getEndy()
+ {
+ return keyArea.y + keyArea.height;
+ }
+
+ /**
+ * Tests if the key is valid in the current flip/slide mode
+ *
+ * @param isFlipSlideClosed True if the flip/slide is currently closed
+ * False if the flip/slide is currently opened
+ *
+ * @return true if the key is valid in the current flip/slide mode; false otherwise
+ */
+ public boolean isFlipSlideValid(boolean isFlipSlideClosed)
+ {
+ boolean flipSlideValid = false;
+
+ if ((flipSlideEnabledCode == FLIP_SLIDE_OPENED_AND_CLOSED)
+ || (isFlipSlideClosed && (flipSlideEnabledCode == FLIP_SLIDE_CLOSED_ONLY))
+ || (!isFlipSlideClosed && (flipSlideEnabledCode == FLIP_SLIDE_OPENED_ONLY)))
+ {
+ flipSlideValid = true;
+ }
+
+ return flipSlideValid;
+ }
+
+ /**
+ * Retrieves the key name
+ *
+ * @return The key name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Retrieves the tool tip text of the key.
+ *
+ * @return The tool tip text of the key.
+ */
+ public String getToolTip()
+ {
+ return toolTip;
+ }
+
+ /**
+ * Retrieves the X coordinate of the upper left corner of the key
+ *
+ * @return X coordinate of the upper left corner of the key
+ */
+ public int getStartx()
+ {
+ return keyArea.x;
+ }
+
+ /**
+ * Retrieves the Y coordinate of the upper left corner of the key
+ *
+ * @return Y coordinate of the upper left corner of the key
+ */
+ public int getStarty()
+ {
+ return keyArea.y;
+ }
+
+ /**
+ * Retrieves a rectangle that represents the key area at skin
+ *
+ * @return A rectangle that represents the key area at skin
+ */
+ public Rectangle getKeyArea()
+ {
+ return keyArea;
+ }
+
+ /**
+ * Tests if the key applies to the provided morphing mode
+ *
+ * @param morphingMode The morphing mode name
+ * @return true if the key applies to the morphing mode; false otherwise
+ */
+ public boolean hasMorphingMode(String morphingMode)
+ {
+ if (morphingMode != null)
+ {
+ return morphingModeCollection.contains(morphingMode);
+ }
+ else
+ {
+ return false;
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/AndroidSkinBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/AndroidSkinBean.java
new file mode 100644
index 0000000..32d7262
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/AndroidSkinBean.java
@@ -0,0 +1,123 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.skin;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * DESCRIPTION:
+ * This bean holds data from the skin.xml file
+ *
+ * RESPONSIBILITY:
+ * - Provide an easy way to retrieve data read from skin.xml files
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Call any of the interface methods to add or retrieve data to the class model
+ */
+public class AndroidSkinBean
+{
+ private final Map<String, Integer> skinPropertiesMap = new HashMap<String, Integer>();
+
+ /**
+ * Adds a skin property to the bean
+ *
+ * @param key The skin property key to use
+ * @param value The value of the skin property
+ */
+ public void addSkinPropertyValue(String key, int value)
+ {
+ skinPropertiesMap.put(key, value);
+ }
+
+ /**
+ * Retrieves a value of a skin property identified by key
+ *
+ * @param key The key that identifies the desired property
+ *
+ * @return The value of the desired property
+ */
+ public int getSkinPropertyValue(String key)
+ {
+ if (skinPropertiesMap.get(key) != null)
+ {
+ return skinPropertiesMap.get(key);
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ /**
+ * Tests if open external display information is available at the skin
+ * which properties are stored at this bean
+ *
+ * @return True if open external display information is available;
+ * false otherwise
+ */
+ public boolean isOpenExternalDisplayAvailable()
+ {
+ boolean result = true;
+ Integer testObj1 = skinPropertiesMap.get(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_WIDTH);
+ Integer testObj2 = skinPropertiesMap.get(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_HEIGHT);
+
+ // If any of the width and height information is not available
+ // it is considered that there is not enough information about
+ // the open external display
+ if ((testObj1 == null) || (testObj2 == null))
+ {
+ result = false;
+ }
+
+ return result;
+ }
+
+ /**
+ * Tests if external display information is available at the skin
+ * which properties are stored at this bean
+ *
+ * @return True if external display information is available;
+ * false otherwise
+ */
+ public boolean isExternalDisplayAvailable()
+ {
+ boolean result = true;
+ Integer testObj1 = skinPropertiesMap.get(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_WIDTH);
+ Integer testObj2 = skinPropertiesMap.get(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_HEIGHT);
+
+ // If any of the width and height information is not available
+ // it is considered that there is not enough information about
+ // the external display
+ if ((testObj1 == null) || (testObj2 == null))
+ {
+ result = false;
+ }
+
+ return result;
+ }
+
+ public double getEmbeddedViewScale()
+ {
+ Integer testObj1 = skinPropertiesMap.get("embeddedViewScale");
+
+ return testObj1.intValue() / 10.0;
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/IAndroidKey.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/IAndroidKey.java
new file mode 100644
index 0000000..9e3237e
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/IAndroidKey.java
@@ -0,0 +1,57 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.skin;
+
+import org.eclipse.swt.graphics.Rectangle;
+
+/**
+ * This interface should be used by anyone who wishes to implement a class
+ * which contain key information for Motorola handsets
+ */
+public interface IAndroidKey
+{
+ /**
+ * This method returns the keysym code associated to this key
+ *
+ * @return The keysym code
+ */
+ String getKeysym();
+
+ /**
+ * This method tests if a certain (x, y) coordinate is inside this key
+ *
+ * @param x The X coordinate
+ * @param y The Y coordinate
+ *
+ * @return true if the provided coordinate is internal to the key; false otherwise
+ */
+ boolean isInsideKey(int x, int y);
+
+ /**
+ * Retrieves a rectangle that corresponds to the drawing area of the
+ * key at the skin
+ *
+ * @return The key area
+ */
+ Rectangle getKeyArea();
+
+ /**
+ * Retrieves the text tool tip of the key.
+ *
+ * @return The text tool tip of the key.
+ */
+ String getToolTip();
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/IAndroidSkin.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/IAndroidSkin.java
new file mode 100644
index 0000000..48b85f6
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/IAndroidSkin.java
@@ -0,0 +1,163 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.skin;
+
+import java.util.Collection;
+import java.util.Properties;
+
+import org.eclipse.sequoyah.vnc.vncviewer.config.EclipsePropertiesFileHandler;
+import org.eclipse.sequoyah.vnc.vncviewer.config.IPropertiesFileHandler;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.RGB;
+
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+
+/**
+ * This interface must be implemented by anyone who wishes to contribute to the
+ * skin extension point
+ */
+public interface IAndroidSkin
+{
+ IPropertiesFileHandler DEFAULT_PROPS_HANDLER = new EclipsePropertiesFileHandler();
+
+ String DEFAULT_VNC_CONFIG_FILE = "resources/vnc_viewer.conf";
+
+ /**
+ * Retrieves an image data object containing one pressed image pixels and attributes
+ *
+ * @return An image data object containing pixels and image attributes
+ *
+ * @throws SkinException If any problem occurs while retrieving the image data.
+ * If a new exception is being created, it is expected that it provides a message
+ * to display to the user
+ */
+ ImageData getPressedImageData(String layoutName) throws SkinException;
+
+ /**
+ * Retrieves an image data object containing one released image pixels and attributes
+ *
+ * @return An image data object containing pixels and image attributes
+ *
+ * @throws SkinException If any problem occurs while retrieving the image data
+ * If a new exception is being created, it is expected that it provides a message
+ * to display to the user
+ */
+ ImageData getReleasedImageData(String layoutName) throws SkinException;
+
+ /**
+ * Retrieves an image data object containing one enter image pixels and attributes
+ *
+ * @return An image data object containing pixels and image attributes
+ *
+ * @throws SkinException If any problem occurs while retrieving the image data
+ * If a new exception is being created, it is expected that it provides a message
+ * to display to the user
+ */
+ ImageData getEnterImageData(String layoutName) throws SkinException;
+
+ /**
+ * Retrieves a collection containing all keys that are supported by the
+ * handset represented by this skin
+ *
+ * @return The key collection read from skin
+ */
+ Collection<IAndroidKey> getKeyDataCollection(String layoutName);
+
+ /**
+ * Retrieves a bean containing all skin data that does not refer to the keys
+ *
+ * @return The skin bean
+ *
+ * @throws SkinException If any problem occurs while retrieving the skin data
+ * If a new exception is being created, it is expected that it provides a message
+ * to display to the user
+ */
+ AndroidSkinBean getSkinBean(String layoutName) throws SkinException;
+
+ /**
+ * Tests if flip is supported by the phone represented by this skin
+ *
+ * @return true if flip is supported; false otherwise
+ */
+ boolean isFlipSupported();
+
+ /**
+ * Set where the skin files are located based on the emulator root dir
+ *
+ * @param emulatorInstallDir Root of emulator installation
+ *
+ * @throws SkinException If the path provided does not contain a valid skin
+ */
+ void setSkinFilesPath(String emulatorInstallDir) throws SkinException;
+
+ /**
+ * Retrieves the names of all available layouts of the skin
+ *
+ * @return A collection containing the names of all available layouts
+ */
+ public Collection<String> getAvailableLayouts();
+
+ /**
+ * Checks if the current layout is rotated (i.e. demands screen rotation)
+ */
+ boolean isSwapWidthHeightNeededAtLayout(String layoutName);
+
+ /**
+ * Retrieves the command to send to the emulator to switch screen
+ *
+ * @return The command to send to the emulator to switch screen
+ */
+ String getLayoutScreenCommand(String layoutName);
+
+ /**
+ * Finds which layout comes next to referenceLayout
+ *
+ * @param referenceLayout The layout to be used as reference on next layout calculation
+ *
+ * @return The next layout name
+ */
+ public String getNextLayout(String referenceLayout);
+
+ /**
+ * Finds which layout is previous to referenceLayout
+ *
+ * @param referenceLayout The layout to be used as reference on previous layout calculation
+ *
+ * @return The previous layout name
+ */
+ public String getPreviousLayout(String referenceLayout);
+
+ /**
+ * Retrieves what is the background color to be applied at the provided layout
+ *
+ * @param layoutName The layout name in which to apply the background color
+ *
+ * @return A RGB object describing the color
+ */
+ public RGB getBackgroundColor(String layoutName);
+
+ /**
+ * @return
+ */
+ Properties getKeyCodes();
+
+ /**
+ * Return the dpad-rotation if present on a given layout or 0 otherwise.
+ * @param layoutName
+ * @return
+ */
+ int getDpadRotation(String layoutName);
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/ISkinFrameworkConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/ISkinFrameworkConstants.java
new file mode 100644
index 0000000..f32a7cb
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/ISkinFrameworkConstants.java
@@ -0,0 +1,33 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.skin;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+/**
+ * Interface containing constants that are relevant to the Android Emulator Skin
+ * Contribution Framework
+ */
+interface ISkinFrameworkConstants
+{
+ String SKIN_EXTENSION_POINT_ID = EmulatorPlugin.PLUGIN_ID + ".skin";
+
+ String SKIN_INFO_ATTR = "skinInfo";
+
+ String SKIN_NAME_ATTR = "skinName";
+
+ String SKIN_ID_ATTR = "skinId";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/ISkinKeyXmlTags.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/ISkinKeyXmlTags.java
new file mode 100644
index 0000000..68fe9fa
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/ISkinKeyXmlTags.java
@@ -0,0 +1,90 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.skin;
+
+/**
+ * Interface that contains the tags used into skin.xml and key.xml files
+ */
+public interface ISkinKeyXmlTags
+{
+ String SKIN_CONFIG = "skinConfig";
+
+ String SKIN_DOUBLE_SCREEN = "doubleScreen";
+
+ String SKIN_DOUBLE_VIEW = "doubleView";
+
+ String SKIN_KEYPAD_AREAS_NUMBER = "keypadAreasNumber";
+
+ String SKIN_VIEW_ZOOM_SCALE = "viewZoomScale";
+
+ String SKIN_MEMORY_SIZE = "memorySize";
+
+ String SKIN_REFRESH_RATE = "refreshRate";
+
+ String SKIN_INTERNAL_VIEW_X = "internalViewX";
+
+ String SKIN_INTERNAL_VIEW_Y = "internalViewY";
+
+ String SKIN_INTERNAL_VIEW_WIDTH = "internalViewWidth";
+
+ String SKIN_INTERNAL_VIEW_HEIGHT = "internalViewHeight";
+
+ String SKIN_INTERNAL_VIEW_DEPTH = "internalViewDepth";
+
+ String SKIN_OPEN_EXTERNAL_VIEW_X = "openExternalViewX";
+
+ String SKIN_OPEN_EXTERNAL_VIEW_Y = "openExternalViewY";
+
+ String SKIN_OPEN_EXTERNAL_VIEW_WIDTH = "openExternalViewWidth";
+
+ String SKIN_OPEN_EXTERNAL_VIEW_HEIGHT = "openExternalViewHeight";
+
+ String SKIN_OPEN_EXTERNAL_COLOR_DEPTH = "openExternalColorDepth";
+
+ String SKIN_EMBEDDED_VIEW_SCALE = "embeddedViewScale";
+
+ String SKIN_EXTERNAL_VIEW_X = "externalViewX";
+
+ String SKIN_EXTERNAL_VIEW_Y = "externalViewY";
+
+ String SKIN_EXTERNAL_VIEW_WIDTH = "externalViewWidth";
+
+ String SKIN_EXTERNAL_VIEW_HEIGHT = "externalViewHeight";
+
+ String SKIN_EXTERNAL_VIEW_DEPTH = "externalViewDepth";
+
+ String KEY_SKIN_KEY_INFO = "skinkeyinfo";
+
+ String KEY_DATA = "keyData";
+
+ String KEY_NAME = "name";
+
+ String KEY_CODE = "keycode";
+
+ String KEY_TOOLTIP = "hint";
+
+ String KEY_START_X = "startx";
+
+ String KEY_START_Y = "starty";
+
+ String KEY_END_X = "endx";
+
+ String KEY_END_Y = "endy";
+
+ String KEY_MORPHING_MODE = "morphingMode";
+
+ String KEY_FLIP_ENABLED = "flipenabled";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/SkinFramework.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/SkinFramework.java
new file mode 100644
index 0000000..77e3e09
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/skin/SkinFramework.java
@@ -0,0 +1,219 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.skin;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.InvalidRegistryObjectException;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+* DESCRIPTION:
+* This class is the entrance point to the Android Emulator Skin Contribution Framework
+* module. Through this, other modules are able to retrieve information about
+* all plugins plugged at com.motorola.studio.android.emulator.skin extension point.
+*
+* RESPONSIBILITY:
+* - Provide information about plugged skins
+*
+* COLABORATORS:
+* None.
+*
+* USAGE:
+* The user gets the instance of SkinFramework and calls one of its
+* public methods to get information regarding installed plugins
+*/
+public class SkinFramework implements ISkinFrameworkConstants
+{
+ /**
+ * A map containing the ids of all plugged skins
+ */
+ private final Map<String, String> skinIdMap = new HashMap<String, String>();
+
+ /**
+ * Creates a new SkinFramework object.
+ */
+ public SkinFramework()
+ {
+ populateSkinIdMap();
+ }
+
+ /**
+ * Retrieves the IDs of every skin that is plugged to this framework
+ *
+ * USAGE: Any client plugin that wishes to have a collection of available
+ * skins should call this method
+ *
+ * @return A collection of installed skin names
+ */
+ public Collection<String> getAllInstalledSkinIds()
+ {
+ return skinIdMap.keySet();
+ }
+
+ /**
+ * Retrieves a skin object identified by the provided ID
+ *
+ * @param skinId The ID of the skin to be retrieved
+ *
+ * @return The skin object that have the ID provided
+ *
+ * @throws SkinException If the skin cannot be loaded by the skin framework
+ */
+ public IAndroidSkin getSkinById(String skinId) throws SkinException
+ {
+ return getSkinById(skinId, null);
+ }
+
+ /**
+ * Retrieves a skin object identified by the provided ID
+ *
+ * @param skinId The ID of the skin to be retrieved
+ *
+ * @param emulatorInstallDir Root of emulator installation
+ *
+ * @return The skin object that have the ID provided
+ *
+ * @throws SkinException If the skin cannot be loaded by the skin framework
+ */
+ public IAndroidSkin getSkinById(String skinId, File emulatorInstallDir) throws SkinException
+ {
+ IAndroidSkin selectedSkin = null;
+
+ if (skinId != null)
+ {
+ try
+ {
+ // If a skin is not found at the already loaded skins collection,
+ // one must be created
+ String extensionId = skinIdMap.get(skinId);
+ if (extensionId != null)
+ {
+ selectedSkin =
+ (IAndroidSkin) EclipseUtils.getExecutable(extensionId, SKIN_INFO_ATTR);
+ if (emulatorInstallDir != null)
+ {
+ selectedSkin.setSkinFilesPath(emulatorInstallDir.getAbsolutePath());
+ }
+ }
+ else
+ {
+ warn("The skin " + skinId
+ + " was requested but not retrieved. It is not installed.");
+ throw new SkinException(NLS.bind(
+ EmulatorNLS.WARN_SkinFramework_SkinNotInstalled, skinId));
+ }
+ }
+ catch (CoreException e)
+ {
+ error("It was not possible to load the IAndroidSkin object associated to " + skinId
+ + " skin. Cause: " + e.getMessage());
+
+ throw new SkinException(NLS.bind(EmulatorNLS.EXC_SkinFramework_CreateIAndroidSkin,
+ skinId));
+ }
+ }
+ else
+ {
+ error("A null parameter as skin name was provided for retrieving a skin object");
+ throw new SkinException(NLS.bind(EmulatorNLS.WARN_SkinFramework_SkinNotInstalled,
+ "\"\""));
+ }
+
+ if (selectedSkin == null)
+ {
+ // If no exception is thrown until this moment and the skin object is still null,
+ // then it is assumed that the plugin has problems. Those are the reasons that explain the assumption:
+ // 1. The only situation in which the EclipseUtils.getExecutable method returns null is
+ // when the provided name is non existent;
+ // 2. SKIN_INFO_ATTR is a constant that matches the constant from the skin extension
+ // specification. If the plugin was loaded even with a different name, Eclipse has failed on detecting
+ // if the declaring plugin was correctly built
+
+ error("The skin plugin is not accordant with the skin extension point specification");
+ throw new SkinException(EmulatorNLS.EXC_SkinFramework_CreateIAndroidSkin);
+ }
+
+ return selectedSkin;
+ }
+
+ /**
+ * Populates the skinIdMap map with the association of each skin name
+ * and its declaring extension identifier
+ */
+ private void populateSkinIdMap()
+ {
+ String skinId;
+ String extensionId;
+
+ IExtension[] skinExtensions = EclipseUtils.getInstalledPlugins(SKIN_EXTENSION_POINT_ID);
+ String currentId = "";
+
+ try
+ {
+ for (IExtension skinExtension : skinExtensions)
+ {
+ currentId = skinExtension.getUniqueIdentifier();
+ IConfigurationElement[] elements = skinExtension.getConfigurationElements();
+
+ for (IConfigurationElement element : elements)
+ {
+ if (element.getName().equals(SKIN_INFO_ATTR))
+ {
+ extensionId = skinExtension.getUniqueIdentifier();
+ skinId = element.getAttribute(SKIN_ID_ATTR);
+ if (skinId != null)
+ {
+ skinIdMap.put(skinId, extensionId);
+ }
+ else
+ {
+ warn("A invalid skin extension was not loaded because it did not declare its ID");
+ String title = EmulatorNLS.GEN_Warning;
+ String message =
+ NLS
+ .bind(
+ EmulatorNLS.WARN_SkinFramework_InvalidInstalledSkinsNotLoaded,
+ currentId);
+ EclipseUtils.showErrorDialog(title, message);
+ }
+ }
+ }
+ }
+ }
+ catch (InvalidRegistryObjectException e)
+ {
+ warn("There are invalid skin extensions that were not loaded due to an exception. Cause: "
+ + e.getMessage());
+ String title = EmulatorNLS.GEN_Warning;
+ EclipseUtils.showErrorDialog(title, NLS.bind(
+ EmulatorNLS.WARN_SkinFramework_InvalidInstalledSkinsNotLoaded, currentId));
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/EmulatorCoreUtils.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/EmulatorCoreUtils.java
new file mode 100644
index 0000000..ec7f5cb
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/EmulatorCoreUtils.java
@@ -0,0 +1,175 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.utils;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewReference;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+import com.motorola.studio.android.emulator.core.exception.InstanceNotFoundException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.model.IEmulatorView;
+
+/**
+ * DESCRIPTION:
+ * Utilities for Android Emulator restricted use
+ *
+ * RESPONSIBILITY:
+ * Provide common utility methods that can be used by any
+ * Android Emulator plugin.
+ *
+ * COLABORATORS:
+ * None
+ *
+ * USAGE:
+ * This class should not be instantiated and its methods should be called statically.
+ */
+public class EmulatorCoreUtils
+{
+ /**
+ * Retrieves all views that implement IEmulatorView interface
+ *
+ * @return All views that implement IEmulatorView interface
+ */
+ public static Collection<IEmulatorView> getAllAndroidViews()
+ {
+ final Collection<IEmulatorView> allViews = new HashSet<IEmulatorView>();
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ IWorkbenchWindow activeWindow =
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (activeWindow != null)
+ {
+ IWorkbenchPage[] activePages = activeWindow.getPages();
+ if (activePages != null)
+ {
+ for (IWorkbenchPage activePage : activePages)
+ {
+ if (activePage != null)
+ {
+ IViewReference[] allReferences = activePage.getViewReferences();
+ for (IViewReference ref : allReferences)
+ {
+ IViewPart aView = ref.getView(false);
+ if ((aView != null) && (aView instanceof IEmulatorView))
+ {
+ allViews.add((IEmulatorView) aView);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+
+ return allViews;
+ }
+
+ /**
+ * Refresh all Emulator views
+ */
+ public static void refreshEmulatorViews()
+ {
+ Collection<IEmulatorView> allViews = EmulatorCoreUtils.getAllAndroidViews();
+ for (IEmulatorView view : allViews)
+ {
+ view.refreshView();
+ }
+ }
+
+ /**
+ * Retrieves a Android Emulator instance mapped by the provided IP address
+ *
+ * @param identifier The IP address associated with the desired instance
+ *
+ * @return The Android Emulator instance that is working on the given address
+ *
+ * @throws InstanceNotFoundException If the instance is not found at
+ * device framework
+ */
+ public static IAndroidEmulatorInstance getAndroidInstanceByIdentifier(String identifier)
+ throws InstanceNotFoundException
+ {
+ IAndroidEmulatorInstance desiredDevice = null;
+
+ Collection<IAndroidEmulatorInstance> instanceList =
+ DeviceFrameworkManager.getInstance().getAllInstances();
+ for (IAndroidEmulatorInstance instance : instanceList)
+ {
+ String instanceIdentifier = instance.getInstanceIdentifier();
+ if ((instanceIdentifier != null) && instanceIdentifier.equals(identifier))
+ {
+ desiredDevice = instance;
+ break;
+ }
+ }
+
+ if (desiredDevice == null)
+ {
+ throw new InstanceNotFoundException();
+ }
+
+ return desiredDevice;
+ }
+
+ /**
+ * Retrieves a Android Emulator instance mapped by the provided
+ * protocol handle object
+ *
+ * @param handle The protocol handle object associated with the
+ * desired instance
+ *
+ * @return The Android Emulator instance that is working with
+ * the given protocol handle object
+ *
+ * @throws InstanceNotFoundException If the instance is not found at
+ * device framework
+ */
+ public static IAndroidEmulatorInstance getAndroidInstanceByHandle(ProtocolHandle handle)
+ throws InstanceNotFoundException
+ {
+ IAndroidEmulatorInstance desiredDevice = null;
+ Collection<IAndroidEmulatorInstance> instanceList =
+ DeviceFrameworkManager.getInstance().getAllInstances();
+ for (IAndroidEmulatorInstance instance : instanceList)
+ {
+ ProtocolHandle instanceHandle = instance.getProtocolHandle();
+ if ((instanceHandle != null) && instanceHandle.equals(handle))
+ {
+ desiredDevice = instance;
+ break;
+ }
+ }
+
+ if (desiredDevice == null)
+ {
+ throw new InstanceNotFoundException();
+ }
+
+ return desiredDevice;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/TelnetAndroidInput.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/TelnetAndroidInput.java
new file mode 100644
index 0000000..f598218
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/TelnetAndroidInput.java
@@ -0,0 +1,416 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.utils;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.core.model.AbstractInputLogic;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.utilities.TelnetFrameworkAndroid;
+
+/**
+ * This class is responsible for sending input events to the Android Emulator
+ */
+@SuppressWarnings("serial")
+public class TelnetAndroidInput extends AbstractInputLogic
+{
+ /*
+ * Event types
+ */
+ public static final String EV_TYPE_SYN = "EV_SYN";
+
+ public static final String EV_TYPE_KEY = "EV_KEY";
+
+ public static final String EV_TYPE_ABS = "EV_ABS";
+
+ public static final String EV_TYPE_SW = "EV_SW";
+
+ public static final int EV_SW_LID = 0x00;
+
+ public static final String EV_SYN_REPORT = "0";
+
+ public static final String EV_ABS_X = "ABS_X";
+
+ public static final String EV_ABS_Y = "ABS_Y";
+
+ private static final String EV_TOUCH = "BTN_TOUCH";
+
+ private final Map<Integer, String> SPECIAL_KEY_MAPPING = new HashMap<Integer, String>()
+ {
+ {
+ put(16777219, "KEY_LEFT"); // left
+ put(16777217, "KEY_UP"); // top
+ put(16777220, "KEY_RIGHT"); // right
+ put(16777218, "KEY_DOWN"); // bottom
+ }
+ };
+
+ /*
+ * Event types
+ */
+ public static final int OPHONE_EV_TYPE_SYN = 0x00;
+
+ public static final int OPHONE_EV_TYPE_KEY = 0x01;
+
+ public static final int OPHONE_EV_TYPE_ABS = 0x03;
+
+ public static final int OPHONE_EV_TYPE_SW = 0x05;
+
+ public static final int OPHONE_EV_SW_LID = 0x00;
+
+ public static final int OPHONE_EV_SYN_REPORT = 0x00;
+
+ public static final int OPHONE_EV_ABS_X = 0x00;
+
+ public static final int OPHONE_EV_ABS_Y = 0x01;
+
+ private final Map<Integer, Integer> OPHONE_SPECIAL_KEY_MAPPING =
+ new HashMap<Integer, Integer>()
+ {
+ {
+ put(16777219, 0x069); // left
+ put(16777217, 0x067); // top
+ put(16777220, 0x06a); // right
+ put(16777218, 0x06c); // bottom
+
+ }
+ };
+
+ private static final int OPHONE_KEY_BACKSPACE = 0x00e;
+
+ private static final int OPHONE_KEY_SPACE = 0x039;
+
+ private static final int OPHONE_KEY_ENTER = 0x01c;
+
+ private static final int OPHONE_EV_TOUCH = 0x14A;
+
+ // mouse position x
+ private int oldX;
+
+ // mouse position y
+ private int oldY;
+
+ // telnet connection
+ private final TelnetFrameworkAndroid telnet = new TelnetFrameworkAndroid();
+
+ /**
+ * Open the Telnet connection and initialize the communication
+ *
+ * @see com.motorola.studio.android.emulator.core.model.AbstractInputLogic#init(com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance)
+ */
+ @Override
+ public void init(IAndroidEmulatorInstance instance)
+ {
+ super.init(instance);
+
+ String deviceSerial = instance.getInstanceIdentifier();
+
+ String serial = deviceSerial.substring(deviceSerial.length() - 4, deviceSerial.length());
+ try
+ {
+ telnet.connect("localhost", Integer.parseInt(serial));
+ }
+ catch (IOException e)
+ {
+ //Do nothing
+ }
+ }
+
+ /**
+ * Close Telnet connection
+ *
+ * @see com.motorola.studio.android.emulator.core.model.AbstractInputLogic#dispose()
+ */
+ @Override
+ public void dispose()
+ {
+ try
+ {
+ telnet.disconnect();
+ }
+ catch (IOException e)
+ {
+ // Do nothing
+ }
+ }
+
+ /**
+ * Send mouse down event
+ *
+ * @see com.motorola.studio.android.emulator.core.model.IInputLogic#sendMouseDown(int, int)
+ */
+ public void sendMouseDown(int x, int y)
+ {
+ sendAndroidEvent(EV_TYPE_ABS, EV_ABS_X, x);
+ sendAndroidEvent(EV_TYPE_ABS, EV_ABS_Y, y);
+ if (SdkUtils.isOphoneSDK())
+ {
+ sendAndroidEvent(EV_TYPE_KEY, OPHONE_EV_TOUCH, true);
+ sendAndroidEvent(EV_TYPE_SYN, OPHONE_EV_SYN_REPORT, 0);
+ }
+ else
+ {
+ sendAndroidEvent(EV_TYPE_KEY, EV_TOUCH, true);
+ sendAndroidEvent(EV_TYPE_SYN, EV_SYN_REPORT, 0);
+ }
+
+ oldX = x;
+ oldY = y;
+ }
+
+ /**
+ * Send mouse up event
+ *
+ * @see com.motorola.studio.android.emulator.core.model.IInputLogic#sendMouseUp(int, int)
+ */
+ public void sendMouseUp(int x, int y)
+ {
+ if (SdkUtils.isOphoneSDK())
+ {
+ sendAndroidEvent(EV_TYPE_KEY, OPHONE_EV_TOUCH, false);
+ sendAndroidEvent(EV_TYPE_SYN, OPHONE_EV_SYN_REPORT, 0);
+ }
+ else
+ {
+ sendAndroidEvent(EV_TYPE_KEY, EV_TOUCH, false);
+ sendAndroidEvent(EV_TYPE_SYN, EV_SYN_REPORT, 0);
+ }
+ }
+
+ /**
+ * Send mouse move event
+ *
+ * @see com.motorola.studio.android.emulator.core.model.IInputLogic#sendMouseMove(int, int)
+ */
+ public void sendMouseMove(int x, int y)
+ {
+ if (oldX != x)
+ {
+ sendAndroidEvent(EV_TYPE_ABS, EV_ABS_X, x);
+ oldX = x;
+ }
+ if (oldY != y)
+ {
+ sendAndroidEvent(EV_TYPE_ABS, EV_ABS_Y, y);
+ oldY = y;
+ }
+ if (SdkUtils.isOphoneSDK())
+ {
+ sendAndroidEvent(EV_TYPE_SYN, OPHONE_EV_SYN_REPORT, 0);
+ }
+ else
+ {
+ sendAndroidEvent(EV_TYPE_SYN, EV_SYN_REPORT, 0);
+ }
+ }
+
+ /**
+ * Send a generic event to the emulator
+ *
+ * @param type event type
+ * @param keysym event definition
+ * @param pressed key pressed - yes or no
+ */
+ public void sendAndroidEvent(String type, String keysym, boolean pressed)
+ {
+ sendAndroidEvent(type, keysym, pressed ? 1 : 0);
+ }
+
+ public void sendAndroidEvent(String type, int keysym, boolean pressed)
+ {
+ sendAndroidEvent(type, keysym, pressed ? 1 : 0);
+ }
+
+ private void sendAndroidEvent(String type, int keysym, int i)
+ {
+ try
+ {
+ telnet.write("event send " + type + ":" + keysym + ":" + i, null);
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Failed to send generic event to Emulator");
+ }
+ }
+
+ /**
+ * Send a complete event to the Emulator
+ * Some events, like a key press, need two events (key pressed/released)
+ * to be executed. This method send both in order to execute the event.
+ *
+ * @param type event type
+ * @param keysym event definition
+ */
+ public void sendAndroidEvent(String type, String keysym)
+ {
+ sendAndroidEvent(type, keysym, true);
+ sendAndroidEvent(type, keysym, false);
+ }
+
+ /**
+ * Send a complete event to the Emulator
+ * Some events, like a key press, need two events (key pressed/released)
+ * to be executed. This method send both in order to execute the event.
+ *
+ * @param type event type
+ * @param keysym event definition
+ */
+ public void sendAndroidEvent(String type, int keysym)
+ {
+ sendAndroidEvent(type, keysym, true);
+ sendAndroidEvent(type, keysym, false);
+ }
+
+ /**
+ * Send a generic event to the emulator
+ *
+ * @param type event type
+ * @param keysym event definition
+ * @param value parameter
+ */
+ private void sendAndroidEvent(String type, String keysym, int value)
+ {
+ try
+ {
+ telnet.write("event send " + type + ":" + keysym + ":" + value, null);
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Failed to send generic event to Emulator");
+ }
+ }
+
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IInputLogic#sendKey(int, int)
+ */
+ public void sendKey(int character, int keycode, Properties keyCodeMap)
+ {
+ /*
+ * Check if it's a character
+ */
+ if (character > 0)
+ {
+ String text = String.valueOf((char) character);
+ // check it's a blank space
+ if (text.equals(" "))
+ {
+ if (SdkUtils.isOphoneSDK())
+ {
+ sendAndroidEvent(EV_TYPE_KEY, OPHONE_KEY_SPACE);
+ }
+ else
+ {
+ sendAndroidEvent(EV_TYPE_KEY, "KEY_SPACE");
+ }
+ }
+ // check if it's a backspace
+ else if (text.equals("\b"))
+ {
+ if (SdkUtils.isOphoneSDK())
+ {
+ sendAndroidEvent(EV_TYPE_KEY, OPHONE_KEY_BACKSPACE);
+ }
+ else
+ {
+ sendAndroidEvent(EV_TYPE_KEY, "KEY_BACKSPACE");
+ }
+ }
+ // check if it's an enter
+ else if (text.equals("\r"))
+ {
+ if (SdkUtils.isOphoneSDK())
+ {
+ sendAndroidEvent(EV_TYPE_KEY, OPHONE_KEY_ENTER);
+ }
+ else
+ {
+ sendAndroidEvent(EV_TYPE_KEY, "KEY_ENTER");
+ }
+ }
+ else
+ {
+ if (keyCodeMap != null)
+ {
+ String keyCode = keyCodeMap.getProperty(text.toUpperCase().trim());
+ if (keyCode != null)
+ {
+ sendAndroidEvent(EV_TYPE_KEY, keyCode);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!SdkUtils.isOphoneSDK())
+ {
+ String keycode_str = null;
+ if (SPECIAL_KEY_MAPPING.containsKey(keycode))
+ {
+ keycode_str = SPECIAL_KEY_MAPPING.get(keycode);
+ }
+ if (keycode_str != null)
+ {
+ sendAndroidEvent(EV_TYPE_KEY, keycode_str);
+ }
+ }
+ else
+ {
+ if (OPHONE_SPECIAL_KEY_MAPPING.containsKey(keycode))
+ {
+ keycode = OPHONE_SPECIAL_KEY_MAPPING.get(keycode);
+ }
+ if (keycode != -1)
+ {
+ sendAndroidEvent(EV_TYPE_KEY, keycode);
+ }
+ }
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IInputLogic#sendClick(int, boolean)
+ */
+ public void sendClick(String code, boolean pressed)
+ {
+ sendAndroidEvent(EV_TYPE_KEY, code, pressed ? 1 : 0);
+ }
+
+ public void sendClick(int code, boolean pressed)
+ {
+ sendAndroidEvent(EV_TYPE_KEY, code, pressed ? 1 : 0);
+ }
+
+ public void sendWindowScale(double zoomFactor)
+ {
+ try
+ {
+ telnet.write("window scale " + zoomFactor, null);
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Failed to send window scale to Emulator");
+ }
+
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/VncAndroidInput.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/VncAndroidInput.java
new file mode 100644
index 0000000..b5383ca
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/core/utils/VncAndroidInput.java
@@ -0,0 +1,96 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.core.utils;
+
+import java.util.Properties;
+
+import org.eclipse.sequoyah.vnc.protocol.PluginProtocolActionDelegate;
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolMessage;
+
+import com.motorola.studio.android.emulator.core.model.AbstractInputLogic;
+
+public class VncAndroidInput extends AbstractInputLogic
+{
+ private static final int VNC_KEYEVENT_MESSAGE_CODE = 0x04;
+
+ private static final int VNC_POINTEREVENT_MESSAGE_CODE = 0x05;
+
+ private boolean buttonPressed;
+
+ private void sendAndroidMouseEventMessage(int x, int y)
+ {
+ ProtocolMessage message = new ProtocolMessage(VNC_POINTEREVENT_MESSAGE_CODE);
+ message.setFieldValue("buttonMask", (buttonPressed ? 1 : 0));
+ message.setFieldValue("x-position", x);
+ message.setFieldValue("y-position", y);
+
+ try
+ {
+ PluginProtocolActionDelegate.sendMessageToServer(getInstance().getProtocolHandle(),
+ message);
+ }
+ catch (Exception e)
+ {
+ // Do nothing
+ }
+ }
+
+ public void sendKey(int character, int keycode, Properties keyCodeMap)
+ {
+ ProtocolMessage message = new ProtocolMessage(VNC_KEYEVENT_MESSAGE_CODE);
+ message.setFieldValue("padding", 0);
+ message.setFieldValue("downFlag", 1);
+ message.setFieldValue("key", keycode);
+
+ try
+ {
+ PluginProtocolActionDelegate.sendMessageToServer(getInstance().getProtocolHandle(),
+ message);
+ }
+ catch (Exception e)
+ {
+ // Do nothing
+ }
+ }
+
+ public void sendClick(int code, boolean pressed)
+ {
+ //do nothing
+ }
+
+ public void sendClick(String code, boolean pressed)
+ {
+ //do nothing
+ }
+
+ public void sendMouseDown(int x, int y)
+ {
+ buttonPressed = true;
+ sendAndroidMouseEventMessage(x, y);
+ }
+
+ public void sendMouseMove(int x, int y)
+ {
+ sendAndroidMouseEventMessage(x, y);
+ }
+
+ public void sendMouseUp(int x, int y)
+ {
+ buttonPressed = false;
+ sendAndroidMouseEventMessage(x, y);
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/AndroidDeviceHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/AndroidDeviceHandler.java
new file mode 100644
index 0000000..da411f5
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/AndroidDeviceHandler.java
@@ -0,0 +1,61 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device;
+
+import org.eclipse.sequoyah.device.framework.model.IDeviceLauncher;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IDeviceHandler;
+
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class represents a TmL IDeviceHandler for Android Emulator Instances.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Create an IInstance object for Android Emulator Device Instances
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * IDeviceHandler: implements this interface
+ * <br>
+ * USAGE:
+ * <br>
+ * This class is declared by the plugin.xml for the Android Emulator Device Instance declaration.
+ */
+public class AndroidDeviceHandler implements IDeviceHandler
+{
+
+ /**
+ * Creates an Android Emulator Device Instance with the given id.
+ *
+ * @param id the instance id
+ */
+ public IInstance createDeviceInstance(String id)
+ {
+ IInstance instance = new AndroidDeviceInstance();
+ instance.setId(id);
+ return instance;
+ }
+
+ public IDeviceLauncher createDeviceLauncher(IInstance instance)
+ {
+ return null;
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/AndroidDeviceUtils.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/AndroidDeviceUtils.java
new file mode 100644
index 0000000..82078c2
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/AndroidDeviceUtils.java
@@ -0,0 +1,74 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent.InstanceEventType;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic.LogicMode;
+
+public class AndroidDeviceUtils
+{
+ public static synchronized void fireDummyStartTransition(AndroidDeviceInstance instance,
+ String serialNumber)
+ {
+ // if instance is not already started, is not starting and is already associated to a VM...
+ boolean instanceStarted = instance.isStarted();
+ boolean instanceIsStarting = instance.getStateMachineHandler().isTransitioning();
+ boolean vmAlreadyUp = instance.hasDevice();
+ instance.setNameSuffix(serialNumber + ", " + instance.getTarget());
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED, instance));
+
+ if (vmAlreadyUp && !instanceStarted && !instanceIsStarting)
+ {
+ info("The TmL Instance is not started/Starting, but the emulator/VM is already online. Execute a dummy start service to force a transition to start status...");
+
+ Map<Object, Object> attributes = new LinkedHashMap<Object, Object>();
+ attributes.put(LogicMode.class, LogicMode.DO_NOTHING);
+ try
+ {
+ EmulatorPlugin.getStartServiceHandler().run(instance, attributes,
+ new NullProgressMonitor());
+ }
+ catch (Exception e)
+ {
+ error("Failed to run the dummy start service on " + instance + " : "
+ + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Verifies whether or not a given AndroidDeviceInstance is running.
+ * @param androidInstance
+ * @return true if instance is running, false otherwise.
+ */
+ public static boolean isInstanceStarting(AndroidDeviceInstance androidInstance)
+ {
+ return androidInstance.getStateMachineHandler().isTransitioning();
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/CreateAVDOnStartupListener.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/CreateAVDOnStartupListener.java
new file mode 100644
index 0000000..13a87da
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/CreateAVDOnStartupListener.java
@@ -0,0 +1,73 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.IStartup;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.emulator.device.handlers.OpenNewDeviceWizardHandler;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+public class CreateAVDOnStartupListener implements IStartup
+{
+ private static final String SDK_CREATE_NEW_AVD_KEY = "create.avd.on.startup";
+
+ private static final Lock lock = new ReentrantReadWriteLock().writeLock();
+
+ private static boolean executed = false;
+
+ public void earlyStartup()
+ {
+ AndroidPlugin.getDefault().addSDKLoaderListener(new Runnable()
+ {
+ public void run()
+ {
+ lock.lock();
+ if (!executed
+ && ((SdkUtils.getAllTargets() != null) && (SdkUtils.getAllTargets().length > 0))
+ && ((SdkUtils.getAllValidVms() != null) && (SdkUtils.getAllValidVms().length == 0)))
+ {
+ if (DialogWithToggleUtils.showQuestion(SDK_CREATE_NEW_AVD_KEY,
+ EmulatorNLS.UI_SdkSetup_CreateAVD_Title,
+ EmulatorNLS.UI_SdkSetup_CreateAVD_Message))
+ {
+ OpenNewDeviceWizardHandler handler = new OpenNewDeviceWizardHandler();
+ try
+ {
+ handler.execute(new ExecutionEvent());
+ }
+ catch (ExecutionException e)
+ {
+ //do nothing
+ lock.unlock();
+ }
+ }
+ executed = true;
+ }
+ lock.unlock();
+ }
+ });
+
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/EmulatorDropSupportHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/EmulatorDropSupportHandler.java
new file mode 100644
index 0000000..81ba24a
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/EmulatorDropSupportHandler.java
@@ -0,0 +1,37 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device;
+
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.swt.dnd.DropTargetEvent;
+import org.eclipse.swt.dnd.TransferData;
+
+import com.motorola.studio.android.devices.AbstractDeviceDropSupportHandler;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+public class EmulatorDropSupportHandler extends AbstractDeviceDropSupportHandler
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.IDeviceTypeDropSupport#canDrop(org.eclipse.sequoyah.device.framework.model.IInstance, org.eclipse.swt.dnd.TransferData, org.eclipse.swt.dnd.DropTargetEvent)
+ */
+ @Override
+ public boolean canDrop(IInstance instance, TransferData data, DropTargetEvent event)
+ {
+ return super.canDrop(instance, data, event)
+ && EmulatorPlugin.STATUS_ONLINE_ID.equals(instance.getStatus());
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/IAndroidDeviceConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/IAndroidDeviceConstants.java
new file mode 100644
index 0000000..bc6c7db
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/IAndroidDeviceConstants.java
@@ -0,0 +1,30 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+/**
+ * DESCRIPTION:
+ * This interface contains constants used by Android Device Plug-in
+ */
+public interface IAndroidDeviceConstants
+{
+ // CONTEXT HELP
+ String MAIN_PAGE_HELP = EmulatorPlugin.PLUGIN_ID + ".newdevmain";
+
+ String STARTUP_OPTIONS_HELP = EmulatorPlugin.PLUGIN_ID + ".newdevstartup";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/IDevicePropertiesConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/IDevicePropertiesConstants.java
new file mode 100644
index 0000000..3064cb4
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/IDevicePropertiesConstants.java
@@ -0,0 +1,132 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device;
+
+import java.io.File;
+
+/**
+ * DESCRIPTION:
+ * This interface contains the name and some default values of the Device Instance Properties
+ */
+public interface IDevicePropertiesConstants
+{
+ /**
+ * The key that identifies the description property of an Android Emulator
+ * device instance
+ */
+ String deviceDescription = "Description";
+
+ /**
+ * The key that identifies the emulator type property of an Android Emulator
+ * device instance
+ */
+ String emulatorDefId = "Emulator_Type";
+
+ /**
+ * The key that identifies the skin property of an Android Emulator
+ * device instance
+ */
+ String skinId = "Skin_Plugin_Id";
+
+ /**
+ * The key that identifies the timeout property of an Android Emulator
+ * device instance
+ */
+ String timeout = "Timeout";
+
+ /**
+ * The key that identifies the useVnc property of an Android Emulator
+ * device instance
+ */
+ String useVnc = "UseVnc";
+
+ /**
+ * The key that identifies the useVnc property of an Android Emulator
+ * device instance
+ */
+ String useProxy = "UseProxy";
+
+ /**
+ * The key that identifies the VM Target property of an Android Emulator
+ * device instance
+ */
+ String vmTarget = "Vm_Target";
+
+ /**
+ * The key that identifies the VM ABI type property of an Android Emulator
+ * device instance
+ */
+ String abiType = "Abi_Type";
+
+ /**
+ * The key that identifies the VM Skin property of an Android Emulator
+ * device instance
+ */
+ String vmSkin = "Vm_Skin";
+
+ /**
+ * The key that identifies the VM Path property of an Android Emulator
+ * device instance
+ */
+ String vmPath = "Vm_Path";
+
+ /**
+ * The key that identifies the command line arguments to be used
+ * when starting the emulator
+ */
+ String commandline = "Command_Line";
+
+ /**
+ * AVD Config file properties
+ */
+ String configSDCardPath = "sdcard.path";
+
+ String configSDCardSize = "sdcard.size";
+
+ /**
+ * The default vm path
+ */
+ String defaultVmPath = System.getProperty("user.home") + File.separator + ".android"
+ + File.separator + "avd";
+
+ String defaultVmFolderSuffix = ".avd";
+
+ String defaultUseProxyValue = "false";
+
+ /**
+ * The default timeout value (ms), which must be used when creating a new emulator
+ * instance
+ */
+ String defaultTimeoutValue = "120";
+
+ /**
+ * Whether to use snapshots
+ */
+
+ String useSnapshots = "UseSnapshot";
+
+ String defaultUseSnapshotValue = "false";
+
+ String saveSnapshot = "SaveSnapshot";
+
+ String dafaultSaveSnapshotValue = "false";
+
+ String startFromSnapshot = "startFromSnapshot";
+
+ String defaultstartFromSnapshotValue = "false";
+
+ String defaulSaveSnapshot = "false";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/SequoyahInstanceBackward.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/SequoyahInstanceBackward.java
new file mode 100644
index 0000000..6ffcce7
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/SequoyahInstanceBackward.java
@@ -0,0 +1,78 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.DevicePlugin;
+import org.eclipse.sequoyah.device.framework.factory.InstanceRegistry;
+import org.eclipse.sequoyah.device.framework.manager.InstanceManager;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.ui.IStartup;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.device.refresh.InstancesListRefresh;
+
+/**
+ * This startup intent to iterate over the list of Android Emulator instances and change the emulator ID
+ * due the change of the plugin ids for the 1.3.0 release
+ *
+ */
+public class SequoyahInstanceBackward implements IStartup
+{
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.IStartup#earlyStartup()
+ */
+ public void earlyStartup()
+ {
+ boolean refreshNeeded = false;
+ List<IInstance> instances =
+ new ArrayList<IInstance>(InstanceRegistry.getInstance().getInstances());
+ for (IInstance oldInstance : instances)
+ {
+ if (oldInstance.getDeviceTypeId().equals(
+ "com.motorola.studio.android.emulator.device.androidDevice"))
+ {
+ try
+ {
+ InstanceRegistry.getInstance().addInstance(
+ InstanceManager.createInstance(oldInstance.getName(),
+ "com.motorola.studio.android.emulator.androidDevice",
+ DevicePlugin.SEQUOYAH_STATUS_OFF, oldInstance.getProperties()));
+ InstanceRegistry.getInstance().removeInstance(oldInstance);
+ }
+ catch (SequoyahException e)
+ {
+ StudioLogger.error(
+ SequoyahInstanceBackward.class,
+ "An error ocurred trying to backward old instance: "
+ + oldInstance.getName(), e);
+ }
+
+ refreshNeeded = true;
+ }
+ }
+ if (refreshNeeded)
+ {
+ InstancesListRefresh.refresh();
+ }
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/SequoyahLogRedirector.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/SequoyahLogRedirector.java
new file mode 100644
index 0000000..2180065
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/SequoyahLogRedirector.java
@@ -0,0 +1,237 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device;
+
+import org.eclipse.sequoyah.device.common.utilities.logger.LoggerConstants;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+
+/**
+ * DESCRIPTION:
+ * This class implements the TmL logger interface to redirect all logs from
+ * TmL to the log system used by the emulator
+ *
+ * RESPONSIBILITY:
+ * Delegate the logging requests from TmL to the same logger used by the emulator
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * An instance of this class is constructed during the emulator log startup.
+ * This class is not supposed to be constructed by clients
+ */
+public class SequoyahLogRedirector implements org.eclipse.sequoyah.vnc.utilities.logger.ILogger,
+ org.eclipse.sequoyah.device.common.utilities.logger.ILogger
+{
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#debug(java.lang.Object)
+ */
+ public void debug(Object message)
+ {
+ if (message instanceof String)
+ {
+ StudioLogger.debug((String) message);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#error(java.lang.Object, java.lang.Object)
+ */
+ public void error(Object message, Object throwable)
+ {
+ if (message instanceof String)
+ {
+ StudioLogger.error((String) message);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#error(java.lang.Object)
+ */
+ public void error(Object message)
+ {
+ if (message instanceof String)
+ {
+ StudioLogger.error((String) message);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#fatal(java.lang.Object)
+ */
+ public void fatal(Object message)
+ {
+ if (message instanceof String)
+ {
+ StudioLogger.fatal((String) message);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#info(java.lang.Object)
+ */
+ public void info(Object message)
+ {
+ if (message instanceof String)
+ {
+ StudioLogger.info((String) message);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#log(java.lang.Object, java.lang.Object, java.lang.Object)
+ */
+ public void log(Object priority, Object message, Object throwable)
+ {
+ log(priority, message);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#log(java.lang.Object, java.lang.Object)
+ */
+ public void log(Object priority, Object message)
+ {
+ String priorityStr = (String) priority;
+ if (message instanceof String)
+ {
+ if (priorityStr.equals(LoggerConstants.FATAL))
+ {
+ StudioLogger.fatal((String) message);
+ }
+ else if (priorityStr.equals(LoggerConstants.ERROR))
+ {
+ StudioLogger.error((String) message);
+ }
+ else if (priorityStr.equals(LoggerConstants.WARNING))
+ {
+ StudioLogger.warn((String) message);
+ }
+ else if (priorityStr.equals(LoggerConstants.INFO))
+ {
+ StudioLogger.info((String) message);
+ }
+ else if (priorityStr.equals(LoggerConstants.DEBUG))
+ {
+ StudioLogger.debug((String) message);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#warn(java.lang.Object)
+ */
+ public void warn(Object message)
+ {
+ if (message instanceof String)
+ {
+ StudioLogger.warn((String) message);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#getCurrentLevel()
+ */
+ public Object getCurrentLevel()
+ {
+ return LoggerConstants.TXT_ALL;
+ }
+
+ //************************************************
+ // FROM THIS POINT, NO METHODS WILL BE IMPLEMENTED
+ //************************************************
+
+ /*
+ *
+ */
+ public void configureLogger(Object arg0)
+ {
+ //nothing to do here
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#log(java.lang.Object)
+ */
+ public void log(Object arg0)
+ {
+ //nothing to do here
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#setLevel(java.lang.Object)
+ */
+ public void setLevel(Object arg0)
+ {
+ //nothing to do here
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#setLogToConsole()
+ */
+ public void setLogToConsole()
+ {
+ //nothing to do here
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#setLogToFile(java.lang.String, java.lang.String)
+ */
+ public void setLogToFile(String arg0, String arg1)
+ {
+ //nothing to do here
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#setLogToFile(java.lang.String)
+ */
+ public void setLogToFile(String arg0)
+ {
+ //nothing to do here
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.vnc.utilities.logger.ILogger#setLogToHTMLFile(java.lang.String)
+ */
+ public void setLogToHTMLFile(String arg0)
+ {
+ //nothing to do here
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.common.utilities.logger.ILogger#setLogToDefault()
+ */
+ public void setLogToDefault()
+ {
+ //nothing to do here
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/TmLDeviceFrameworkSupport.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/TmLDeviceFrameworkSupport.java
new file mode 100644
index 0000000..1d400cd
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/TmLDeviceFrameworkSupport.java
@@ -0,0 +1,88 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.DeviceUtils;
+import org.eclipse.sequoyah.device.framework.factory.InstanceRegistry;
+import org.eclipse.sequoyah.device.framework.model.IDeviceType;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.IService;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.emulator.core.devfrm.IDeviceFrameworkSupport;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+
+/**
+ * DESCRIPTION:
+ * This class attaches the TmL device framework to the Android Emulator plug-ins
+ *
+ * RESPONSIBILITY:
+ * to work with the TmL device framework
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * The class should be used by Eclipse only
+ */
+public class TmLDeviceFrameworkSupport implements IDeviceFrameworkSupport
+{
+ /**
+ * @see IDeviceFrameworkSupport#getAllInstances()
+ */
+ public Collection<IAndroidEmulatorInstance> getAllInstances()
+ {
+ List<IInstance> tmlInstances = InstanceRegistry.getInstance().getInstances();
+ Collection<IAndroidEmulatorInstance> androidCollection =
+ new HashSet<IAndroidEmulatorInstance>();
+ for (IInstance tmlInstance : tmlInstances)
+ {
+ if (tmlInstance instanceof IAndroidEmulatorInstance)
+ {
+ androidCollection.add((IAndroidEmulatorInstance) tmlInstance);
+ }
+ }
+
+ return androidCollection;
+ }
+
+ // This should be a contribution to TmL.
+ public static IStatus runService(IInstance instance, String serviceID,
+ Map<Object, Object> arguments, IProgressMonitor monitor) throws SequoyahException
+ {
+ IStatus runStatus = null;
+ IDeviceType deviceType = DeviceUtils.getDeviceType(instance);
+ for (IService service : deviceType.getServices())
+ {
+ if (service.getId().equals(serviceID))
+ {
+ ServiceHandler serviceHandler = (ServiceHandler) service.getHandler();
+ runStatus = serviceHandler.run(instance, arguments, monitor);
+ break;
+ }
+ }
+
+ return runStatus;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/AndroidEmuDefBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/AndroidEmuDefBean.java
new file mode 100644
index 0000000..7f3c898
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/AndroidEmuDefBean.java
@@ -0,0 +1,102 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.definition;
+
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * This class holds an emulator definition
+ *
+ */
+class AndroidEmuDefBean
+{
+
+ // emulator name
+ private String name;
+
+ // emulator skin ID
+ private String skinId;
+
+ // emulator start logic
+ private AbstractStartAndroidEmulatorLogic startLogic = null;
+
+ // startup emulator command arguments
+ private String arguments = NativeUIUtils.getDefaultCommandLine();
+
+ /**
+ * Create an emulator definition
+ *
+ * @param name emulator name
+ * @param skinId emulator skin ID
+ * @param skinSize emulator skin size
+ */
+ AndroidEmuDefBean(String name, String skinId, String skinSize)
+ {
+ this.name = name;
+ this.skinId = skinId;
+ }
+
+ /**
+ * Get emulator name
+ *
+ * @return emulator name
+ */
+ String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Get emulator skin ID
+ *
+ * @return emulator skin ID
+ */
+ String getSkinId()
+ {
+ return skinId;
+ }
+
+ /**
+ * Get startup emulator command arguments
+ *
+ * @return emulator command line arguments
+ */
+ String getCommandLineArguments()
+ {
+ return arguments;
+ }
+
+ /**
+ * Get emulator start logic
+ *
+ * @return emulator start logic
+ */
+ public AbstractStartAndroidEmulatorLogic getStartLogic()
+ {
+ return startLogic;
+ }
+
+ /**
+ * Set emulator start logic
+ * @param startLogic emulator start logic class
+ */
+ public void setStartLogic(AbstractStartAndroidEmulatorLogic startLogic)
+ {
+ this.startLogic = startLogic;
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/AndroidEmuDefMgr.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/AndroidEmuDefMgr.java
new file mode 100644
index 0000000..a1eeada
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/AndroidEmuDefMgr.java
@@ -0,0 +1,265 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.definition;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.sequoyah.device.common.utilities.PluginUtils;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.model.IInputLogic;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic;
+
+/**
+ * Contains methods for managing Android emulator definitions
+ *
+ */
+public class AndroidEmuDefMgr implements IAndroidEmuDefConstants
+{
+ // emulator definitions
+ private static final Map<String, AndroidEmuDefBean> emuDefs =
+ new LinkedHashMap<String, AndroidEmuDefBean>();
+
+ private final static AndroidEmuDefMgr instance = new AndroidEmuDefMgr();
+
+ /**
+ * Initialize class
+ */
+ private AndroidEmuDefMgr()
+ {
+ readExtensions();
+ }
+
+ /**
+ * Return class instance
+ *
+ * @return AndroidEmuDefMgr instance
+ */
+ public static AndroidEmuDefMgr getInstance()
+ {
+ return instance;
+ }
+
+ /**
+ * Read extension points to add emulator definitions
+ */
+ private static void readExtensions()
+ {
+ IExtension[] emuDefExtensions =
+ EclipseUtils.getInstalledPlugins(EMULATOR_DEFINITION_EXTENSION_POINT);
+
+ for (IExtension emuDefExtension : emuDefExtensions)
+ {
+ String id = emuDefExtension.getUniqueIdentifier();
+
+ // elements
+ IConfigurationElement[] elements = emuDefExtension.getConfigurationElements();
+
+ AndroidEmuDefBean bean = null;
+
+ boolean extensionOk = true;
+ for (IConfigurationElement element : elements)
+ {
+ if (element.getName().equals(ELEMENT_SKIN))
+ {
+ String skinId = element.getAttribute(ATT_SKIN_ID);
+ String skinSize = element.getAttribute(ATT_SKIN_SIZE);
+
+ if (!skinId.equals("") && !skinSize.equals(""))
+ {
+ bean = new AndroidEmuDefBean(emuDefExtension.getLabel(), skinId, skinSize);
+ }
+ else
+ {
+ extensionOk = false;
+ }
+ }
+ }
+
+ if (extensionOk)
+ {
+ emuDefs.put(id, bean);
+ }
+ }
+ }
+
+ /**
+ * Get all emulator IDs
+ *
+ * @return all emulator IDs
+ */
+ public Collection<String> getAllIds()
+ {
+ return emuDefs.keySet();
+ }
+
+ /**
+ * Retrieves the default emulator definition id, which should be initially set
+ * to the emulator devices being created
+ *
+ * @return The default emulator definition id
+ */
+ public String getDefaultId()
+ {
+ String defaultId = "";
+
+ Collection<String> ids = getAllIds();
+
+ /*
+ * NOTE: This is not considering more than one type of device
+ */
+ if (!ids.isEmpty())
+ {
+ Object[] idsArray = ids.toArray();
+ defaultId = idsArray[0].toString();
+ }
+
+ return defaultId;
+ }
+
+ /**
+ * Get all emulator names
+ *
+ * @return all emulator names
+ */
+ public String[] getAllNames()
+ {
+ String[] allNames = new String[emuDefs.size()];
+
+ int i = 0;
+ for (AndroidEmuDefBean bean : emuDefs.values())
+ {
+ allNames[i++] = bean.getName();
+ }
+
+ return allNames;
+ }
+
+ /**
+ * Get emulator name given its ID
+ *
+ * @param emuDefId emulator ID
+ * @return emulator name
+ */
+ public String getName(String emuDefId)
+ {
+ String name = emuDefId;
+ AndroidEmuDefBean bean = emuDefs.get(emuDefId);
+
+ if (bean != null)
+ {
+ name = bean.getName();
+ }
+
+ return name;
+ }
+
+ /**
+ * Get emulator skin ID given its ID
+ *
+ * @param emuDefId emulator ID
+ * @return emulator skin ID
+ */
+ public String getSkinId(String emuDefId)
+ {
+ String skinId = "";
+ AndroidEmuDefBean bean = emuDefs.get(emuDefId);
+
+ if (bean != null)
+ {
+ skinId = bean.getSkinId();
+ }
+
+ return skinId;
+ }
+
+ /**
+ * Get startup emulator command arguments
+ *
+ * @param emuDefId emulator ID
+ * @return emulator command line arguments
+ */
+ public String getCommandLineArgumentsForEmuDefinition(String emuDefId)
+ {
+ String arguments = "";
+ AndroidEmuDefBean bean = emuDefs.get(emuDefId);
+
+ if (bean != null)
+ {
+ arguments = bean.getCommandLineArguments();
+ }
+
+ return arguments;
+ }
+
+ /**
+ * Retrieve the input logic of the given emulator definition
+ * @param emuDefId id of the extension that declare emulator definitions.
+ * @return the IAndroidLogic associated to the given emulator definitions
+ */
+ public IInputLogic getInputLogic(String emuDefId, IAndroidEmulatorInstance instance)
+ {
+
+ IInputLogic inputLogic = null;
+
+ try
+ {
+ inputLogic = (IInputLogic) PluginUtils.getExecutable(emuDefId, "inputLogic");
+ inputLogic.init(instance);
+ }
+ catch (Exception e)
+ {
+ error("Could not retrieve the input logic from definition " + emuDefId);
+ }
+
+ return inputLogic;
+ }
+
+ /**
+ * Get the start logic for the given emulator definition
+ * @param emuDefId id of the extension that declare emulator definitions.
+ * @return the IAndroidLogic associated to the given emulator definitions
+ */
+ public AbstractStartAndroidEmulatorLogic getStartLogic(String emuDefId)
+ {
+
+ AbstractStartAndroidEmulatorLogic startLogic = null;
+ AndroidEmuDefBean bean = emuDefs.get(emuDefId);
+
+ try
+ {
+ if (bean.getStartLogic() == null)
+ {
+ bean.setStartLogic((AbstractStartAndroidEmulatorLogic) PluginUtils.getExecutable(
+ emuDefId, "startLogic"));
+ }
+ startLogic = bean.getStartLogic();
+ }
+ catch (Exception e)
+ {
+ error("Could not retrieve the Start logic for " + emuDefId);
+ }
+
+ return startLogic;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/IAndroidEmuDefConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/IAndroidEmuDefConstants.java
new file mode 100644
index 0000000..92ef40a
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/definition/IAndroidEmuDefConstants.java
@@ -0,0 +1,42 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.definition;
+
+/**
+ * This interface contains constants used when managing Android emulator definitions
+ *
+ */
+public interface IAndroidEmuDefConstants
+{
+ String EMULATOR_DEFINITION_EXTENSION_POINT =
+ "com.motorola.studio.android.emulator.androidEmulatorDefinition";
+
+ String ELEMENT_SKIN = "skin";
+
+ String ATT_SKIN_ID = "id";
+
+ String ATT_SKIN_SIZE = "size";
+
+ String SKIN_SIZE_HVGA = "HVGA";
+
+ String SKIN_SIZE_HVGAL = "HVGA-L";
+
+ String SKIN_SIZE_HVGAP = "HVGA-P";
+
+ String SKIN_SIZE_QVGAL = "QVGA-L";
+
+ String SKIN_SIZE_QVGAP = "QVGA-P";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/handlers/OpenNewDeviceWizardHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/handlers/OpenNewDeviceWizardHandler.java
new file mode 100644
index 0000000..64c295b
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/handlers/OpenNewDeviceWizardHandler.java
@@ -0,0 +1,52 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.sequoyah.device.framework.ui.wizard.NewDeviceMenuWizard;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+public class OpenNewDeviceWizardHandler extends AbstractHandler
+{
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable()
+ {
+
+ public void run()
+ {
+ NewDeviceMenuWizard wizard = new NewDeviceMenuWizard();
+ wizard.setCurrentDeviceTypeId(EmulatorPlugin.DEVICE_ID);
+ WizardDialog dialog =
+ new WizardDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+ .getShell(), wizard);
+ dialog.open();
+ }
+ });
+
+ return null;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/init/InitServiceHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/init/InitServiceHandler.java
new file mode 100644
index 0000000..a79d214
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/init/InitServiceHandler.java
@@ -0,0 +1,145 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.init;
+
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent.InstanceEventType;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.IService;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+
+/**
+ * DESCRIPTION:
+ * This class plugs the init procedure to a TmL service. This service implements the
+ * interface directly, because it causes the instance to have a particular behavior
+ * at the state machine.
+ *
+ * RESPONSIBILITY:
+ * Provide the initialization procedure to apply to every instance that
+ * is loaded at TmL device framework
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by Eclipse only
+ */
+public class InitServiceHandler implements IServiceHandler
+{
+ /**
+ * The parent service handler
+ */
+ private IServiceHandler parent;
+
+ /**
+ * The service that launches the handler
+ */
+ private IService service;
+
+ /**
+ * @see IServiceHandler#run(IInstance)
+ */
+ public void run(IInstance instance)
+ {
+ if (instance instanceof IAndroidLogicInstance)
+ {
+ // The service definition defined (by convention) that
+ // stopped-dirty is the success state, and not available
+ // is the failure state. The exception is being thrown for
+ // the framework to set the state correctly.
+ instance.setStatus(EmulatorPlugin.STATUS_NOT_AVAILABLE);
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_TRANSITIONED, instance));
+ }
+
+ }
+
+ /**
+ * @see IServiceHandler#newInstance()
+ */
+ public IServiceHandler newInstance()
+ {
+ return new InitServiceHandler();
+ }
+
+ /**
+ * @see IServiceHandler#setParent(IServiceHandler)
+ */
+ public void setParent(IServiceHandler handler)
+ {
+ this.parent = handler;
+ }
+
+ /**
+ * @see IServiceHandler#setService(IService)
+ */
+ public void setService(IService service)
+ {
+ this.service = service;
+ }
+
+ /**
+ * @see IServiceHandler#updatingService(IInstance)
+ */
+ public void updatingService(IInstance instance)
+ {
+ info("Updating init emulator service");
+ }
+
+ /**
+ * @see IServiceHandler#clone()
+ * @see Cloneable#clone()
+ */
+ @Override
+ public Object clone()
+ {
+ IServiceHandler newHandler = newInstance();
+ newHandler.setParent(parent);
+ newHandler.setService(service);
+ return newHandler;
+ }
+
+ /**
+ * @see IServiceHandler#getParent()
+ */
+ public IServiceHandler getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * @see IServiceHandler#getService()
+ */
+ public IService getService()
+ {
+ return service;
+ }
+
+ public IStatus singleInit(List<IInstance> instances)
+ {
+ return Status.OK_STATUS;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDevInstBuilder.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDevInstBuilder.java
new file mode 100644
index 0000000..cee670c
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDevInstBuilder.java
@@ -0,0 +1,108 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.instance;
+
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.sequoyah.device.framework.model.IInstanceBuilder;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class represents a TmL IInstanceBuilder for Android Emulator Instances.
+ * <br>
+ * It is only necessary because TmL's AbstractNewEmulatorInstanceWizard could not be used
+ * for Android Emulator Device Instance New Wizard implementation, so an IInstanceBuilder
+ * became necessary for our Wizard implementation.
+ * <br>
+ * This class should be removed if TmL's Wizard begins to be used since this implementation
+ * is very similar to TmL DefaultInstanceBuilder implementation and Tml's Wizard would use
+ * it instead.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Hold necessary information about a Android Emulator Device Instance for it to be
+ * created.
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * IInstanceBuilder: implements this interface
+ * <br>
+ * USAGE:
+ * <br>
+ * The AndroidNewWizard uses this class to hold information for creating a Android Emulator Device
+ * Instance and passes it on to TmL's InstanceManager to carry on the creation.
+ */
+public class AndroidDevInstBuilder implements IInstanceBuilder
+{
+ private final Properties properties;
+
+ private final String name;
+
+ /**
+ * Creates a new Instance Builder with the given information.
+ *
+ * @param instanceName the name of the instance to be created using this builder
+ * @param properties the properties of the instance to be created using this builder
+ */
+ public AndroidDevInstBuilder(String instanceName, Properties properties)
+ {
+ this.properties = properties;
+ this.name = instanceName;
+ }
+
+ /**
+ * Always returns <code>null</code> since this information does
+ * not make sense for Android Emulator Instances.
+ */
+ public IPath getLocationPath()
+ {
+ return null;
+ }
+
+ /**
+ * Retrieves the name of the instance to be created using this builder
+ *
+ * @return the name of the instance to be created using this builder
+ */
+ public String getProjectName()
+ {
+ return name;
+ }
+
+ /**
+ * Retrieves the properties of the instance to be created using this builder
+ *
+ * @return the properties of the instance to be created using this builder
+ */
+ public Properties getProperties()
+ {
+ return properties;
+ }
+
+ /**
+ * Retrieves the value of the give property key.
+ *
+ * @param key the key of the property
+ *
+ * @return the value for the property for the instance to be created using this builder
+ */
+ public String getProperty(String key)
+ {
+ return properties.getProperty(key);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDevInstListener.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDevInstListener.java
new file mode 100644
index 0000000..8e9ff9b
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDevInstListener.java
@@ -0,0 +1,130 @@
+/* Copyright (C) 2012 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.motorola.studio.android.emulator.device.instance;
+
+import org.eclipse.sequoyah.device.framework.DevicePlugin;
+import org.eclipse.sequoyah.device.framework.events.IInstanceListener;
+import org.eclipse.sequoyah.device.framework.events.InstanceAdapter;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.utils.EmulatorCoreUtils;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+
+/**
+ * DESCRIPTION:
+ * Implementation of IInstanceListener for device related actions that depend
+ * on the TmL instance registry state.
+ * <br>
+ * RESPONSIBILITY:
+ * Guarantee that the emulator views are updated
+ * Run the initialization service when an instance is loaded
+ * <br>
+ * COLABORATORS:
+ * None.
+ * <br>
+ * USAGE:
+ * This class shall be used by Eclipse only.
+ */
+public class AndroidDevInstListener extends InstanceAdapter
+{
+
+ /**
+ * @see IInstanceListener#instanceLoaded(InstanceEvent)
+ */
+ @Override
+ public void instanceLoaded(InstanceEvent e)
+ {
+ IInstance instance = e.getInstance();
+
+ if (instance instanceof IAndroidEmulatorInstance)
+ {
+ // The service definition defined (by convention) that
+ // stopped-dirty is the success state, and not available
+ // is the failure state. The exception is being thrown for
+ // the framework to set the state correctly.
+ if (instance.getStatus().equals(DevicePlugin.SEQUOYAH_STATUS_OFF))
+ {
+ instance.setStatus(EmulatorPlugin.STATUS_NOT_AVAILABLE);
+ }
+ }
+ }
+
+ /**
+ * @see IInstanceListener#instanceDeleted(InstanceEvent)
+ */
+ @Override
+ public void instanceDeleted(InstanceEvent ev)
+ {
+ IInstance instance = ev.getInstance();
+ if (instance instanceof AndroidDeviceInstance)
+ {
+ SdkUtils.deleteVm(instance.getName());
+ }
+ }
+
+ /**
+ * @see IInstanceListener#instanceTransitioned(InstanceEvent)
+ */
+ @Override
+ public void instanceTransitioned(InstanceEvent e)
+ {
+ IInstance instance = e.getInstance();
+
+ if (instance instanceof AndroidDeviceInstance)
+ {
+ final AndroidDeviceInstance androidDevice = (AndroidDeviceInstance) instance;
+ StudioLogger.info("The android device instance status was updated: " + instance
+ + " Status: " + instance.getStatus());
+
+ if (androidDevice.isStarted())
+ {
+ String transitionId = e.getTransitionId();
+ if ((transitionId != null)
+ && transitionId.equals("com.motorola.studio.android.emulator.startService"))
+ {
+ // If it is coming from other state than the started,
+ // connect to VNC server
+ StudioLogger
+ .info("The emulator "
+ + instance
+ + " transitioned to started state. Try to estabilish a VNC connection...");
+
+ new Thread(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ AbstractAndroidView.showView();
+ EmulatorCoreUtils.refreshEmulatorViews();
+ }
+ }).start();
+ }
+ }
+ else if (instance.getStatus().equals(EmulatorPlugin.STATUS_OFFLINE))
+ {
+ androidDevice.resetRuntimeVariables();
+ EmulatorCoreUtils.refreshEmulatorViews();
+ }
+
+ }
+
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDeviceInstance.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDeviceInstance.java
new file mode 100644
index 0000000..b08f556
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/AndroidDeviceInstance.java
@@ -0,0 +1,776 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.instance;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.sequoyah.device.framework.model.AbstractMobileInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+import org.eclipse.sequoyah.vnc.protocol.PluginProtocolActionDelegate;
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.internal.avd.AvdInfo;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.model.IInputLogic;
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.device.definition.AndroidEmuDefMgr;
+import com.motorola.studio.android.emulator.device.instance.options.StartupOptionsMgt;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic;
+import com.motorola.studio.android.emulator.logic.AndroidLogicUtils;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+import com.motorola.studio.android.emulator.logic.stop.AndroidEmulatorStopper;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * DESCRIPTION:
+ * This class represents a Android Emulator instance
+ *
+ * RESPONSIBILITY:
+ * - Hold all attributes of an Android Emulator instance
+ * - Provide methods for testing if started and to stop the instance
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is not meant to be used directly by the user. All commands to the
+ * Android Emulator instance shall be provided through the device framework.
+ */
+public class AndroidDeviceInstance extends AbstractMobileInstance implements IAndroidLogicInstance,
+ IWorkbenchAdapter, ISerialNumbered
+{
+ /**
+ * The protocol that is executed by this instance
+ */
+ private ProtocolHandle handle;
+
+ /**
+ * True if this instance has and supports CLI display; false otherwise
+ */
+ private boolean hasCli = false;
+
+ /**
+ * Current layout of this instance
+ */
+ private String currentLayout;
+
+ private Process process;
+
+ private long windowHandle;
+
+ private Composite composite;
+
+ /**
+ * Tests if this instance is in started or stopped state
+ *
+ * @return true if started, false if stopped
+ */
+ public boolean isStarted()
+ {
+ boolean instanceStarted = false;
+ String status = getStatus();
+ if (EmulatorPlugin.STATUS_ONLINE_ID.equals(status))
+ {
+ instanceStarted = true;
+ }
+
+ return instanceStarted;
+ }
+
+ public boolean isConnected()
+ {
+ if (super.getProperties()
+ .getProperty(IDevicePropertiesConstants.useVnc, NativeUIUtils.getDefaultUseVnc())
+ .equals("true"))
+ {
+ ProtocolHandle protocolHandle = getProtocolHandle();
+ if (protocolHandle != null)
+ {
+ return PluginProtocolActionDelegate.isProtocolRunning(protocolHandle);
+ }
+ return false;
+ }
+ else
+ {
+ return isStarted();
+ }
+
+ }
+
+ /**
+ * Method used by the emulator core to stop the instance on errors
+ *
+ * @see IAndroidEmulatorInstance#stop(boolean)
+ *
+ * @param force True if no interaction with the user is desired to
+ * perform the stop operation; false otherwise
+ */
+ public void stop(boolean force) throws InstanceStopException
+ {
+
+ info("Stopping the Android Emulator instance: " + this);
+
+ // FIRST SCENARIO: THE INSTANCE IS STARTED AND FAILED DURING OPERATION
+ // If the instance is in started state, the stop handler is called to delegate the state
+ // maintenance to TmL, which is correct.
+ if (isStarted())
+ {
+ if (getStateMachineHandler().isTransitioning())
+ {
+ // Free other threads to continue their jobs if one is already running the procedure
+ info("The instance is already executing a stop process: " + this);
+ throw new InstanceStopException("The instance is already executing a stop process");
+ }
+ else
+ {
+ info("Instance is started. Run the regular transition to stopped status: " + this);
+ ServiceHandler stopHandler = EmulatorPlugin.getStopServiceHandler();
+ if (stopHandler != null)
+ {
+ try
+ {
+ Map<Object, Object> args = new HashMap<Object, Object>();
+ args.put(EmulatorPlugin.FORCE_ATTR, true);
+ stopHandler.run(this, args);
+ }
+ catch (Exception e)
+ {
+ // Should not enter here, because the state has been tested before
+ error("The instance is not in an appropriate state for stopping");
+ }
+ info("Finished stop process: " + this);
+ }
+ }
+ }
+ // SECOND SCENARIO: THE INSTANCE WAS STARTING AND FAILED DURING THE PROCESS
+ // If the instance is not in started state, TmL will not allow the stop service to run. In
+ // this case, the methods must be called manually, and the state does not need maintenance
+ // (it has never been updated, as updating happens in the end of a transition)
+ else
+ {
+ if (getStateMachineHandler().isTransitioning())
+ {
+
+ info("Instance is not fully started yet. Execute a stop process directly..." + this);
+ final Job haltJob = new Job(EmulatorNLS.UI_AndroidDeviceInstance_StopInstanceJob)
+ {
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+ AndroidEmulatorStopper.stopInstance(AndroidDeviceInstance.this, true, true,
+ monitor);
+
+ if (getStatus().equals(EmulatorPlugin.STATUS_OFFLINE_NO_DATA))
+ {
+ info("Instance was initially in stopped/clean status. Rollback if needed."
+ + this);
+
+ File userdataFile = getUserdata();
+ if ((userdataFile != null) && userdataFile.exists())
+ {
+ info("Deleted data created during the start tentative."
+ + userdataFile);
+ userdataFile.delete();
+ }
+ }
+
+ info("Finished stop process: " + AndroidDeviceInstance.this);
+ return Status.OK_STATUS;
+ }
+ };
+ haltJob.schedule();
+ }
+ }
+ }
+
+ /**
+ *
+ */
+ @Override
+ public Properties getProperties()
+ {
+ Properties properties = super.getProperties();
+
+ // synchronize instance properties data with current sdk...
+ File fromSdk = SdkUtils.getUserdataDir(getName());
+ if (fromSdk != null)
+ {
+ properties.put(IDevicePropertiesConstants.vmPath, fromSdk.getAbsolutePath());
+ }
+
+ return properties;
+ }
+
+ /**
+ * @see IAndroidEmulatorInstance#getHasCli()
+ */
+ public boolean getHasCli()
+ {
+ return hasCli;
+ }
+
+ /**
+ * @see IAndroidEmulatorInstance#getInstanceIdentifier()
+ */
+ public String getInstanceIdentifier()
+ {
+ return getSerialNumber();
+ }
+
+ /**
+ * @see IAndroidEmulatorInstance#getProtocolHandle()
+ */
+ public ProtocolHandle getProtocolHandle()
+ {
+ return handle;
+ }
+
+ /**
+ * @see IAndroidEmulatorInstance#getEmulatorDefId()
+ */
+ public String getEmulatorDefId()
+ {
+ return getProperties().getProperty(IDevicePropertiesConstants.emulatorDefId);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance#getCurrentLayout()
+ */
+ public String getCurrentLayout()
+ {
+ return currentLayout;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance#setCurrentLayout(java.lang.String)
+ */
+ public void setCurrentLayout(String layoutName)
+ {
+ currentLayout = layoutName;
+ }
+
+ /**
+ * @see IAndroidEmulatorInstance#setHasCli(boolean)
+ */
+ public void setHasCli(boolean hasCli)
+ {
+ this.hasCli = hasCli;
+ }
+
+ /**
+ * @see IAndroidEmulatorInstance#setProtocolHandle(ProtocolHandle)
+ */
+ public void setProtocolHandle(ProtocolHandle handle)
+ {
+ this.handle = handle;
+ }
+
+ /**
+ * Resets all the previous runtime state to clean them for next execution
+ */
+ void resetRuntimeVariables()
+ {
+ handle = null;
+ hasCli = false;
+ currentLayout = null;
+ }
+
+ /**
+ * This version of getAdapter needs to assure that only an Android
+ * Device instance is compatible with itself
+ *
+ * @see IAdaptable#getAdapter(Class)
+ */
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Object getAdapter(Class adapter)
+ {
+ if (adapter.isInstance(this))
+ {
+ return this;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Retrieves the default properties to be used upon instance creation
+ *
+ * @param defaultProperties property object to be filled
+ * @param vmSkin Android VM skin
+ */
+ public static void populateWithDefaultProperties(Properties defaultProperties)
+ {
+
+ AndroidEmuDefMgr emuDefMgr = AndroidEmuDefMgr.getInstance();
+
+ // When removing the emulator definition extension, remove this hardcoded set as
+ // well as the constant declaration from the Activator. If the Mot skin plugin is used,
+ // find another way to set this variable
+ String emuDefId = EmulatorPlugin.DEFAULT_EMULATOR_DEFINITION;
+
+ // Emulator Definition
+ defaultProperties.setProperty(IDevicePropertiesConstants.emulatorDefId, emuDefId);
+
+ // skin from Emulator Definition
+ defaultProperties.setProperty(IDevicePropertiesConstants.skinId,
+ emuDefMgr.getSkinId(emuDefId));
+
+ // command line arguments from Emulator Definition
+ defaultProperties.setProperty(IDevicePropertiesConstants.commandline,
+ emuDefMgr.getCommandLineArgumentsForEmuDefinition(emuDefId));
+
+ // default timeout
+ defaultProperties.setProperty(IDevicePropertiesConstants.timeout,
+ IDevicePropertiesConstants.defaultTimeoutValue);
+
+ //default useVnc
+ defaultProperties.setProperty(IDevicePropertiesConstants.useVnc,
+ NativeUIUtils.getDefaultUseVnc());
+
+ //default useProxy
+ defaultProperties.setProperty(IDevicePropertiesConstants.useProxy,
+ IDevicePropertiesConstants.defaultUseProxyValue);
+ }
+
+ /**
+ * Populate VM Target and VM skin
+ *
+ * @param instanceName
+ * @param instanceProperties
+ */
+ public static void populateWithVMInfo(String instanceName, Properties instanceProperties)
+ {
+ AvdInfo vmInfo = SdkUtils.getValidVm(instanceName);
+
+ if (vmInfo != null)
+ {
+ // VM target
+ instanceProperties.setProperty(IDevicePropertiesConstants.vmTarget, vmInfo.getTarget()
+ .getName());
+
+ // ABI Type
+ instanceProperties.setProperty(IDevicePropertiesConstants.abiType, vmInfo.getAbiType());
+
+ // VM skin
+ instanceProperties.setProperty(IDevicePropertiesConstants.vmSkin,
+ SdkUtils.getSkin(vmInfo));
+
+ // VM path
+ instanceProperties.setProperty(IDevicePropertiesConstants.vmPath,
+ vmInfo.getDataFolderPath());
+ String useSnapShot = vmInfo.getProperties().get("snapshot.present");
+ if (useSnapShot == null)
+ {
+ useSnapShot = "false";
+ }
+
+ instanceProperties.setProperty(IDevicePropertiesConstants.useSnapshots, useSnapShot);
+ if (instanceProperties.getProperty(IDevicePropertiesConstants.startFromSnapshot) == null)
+ {
+ instanceProperties.setProperty(IDevicePropertiesConstants.startFromSnapshot,
+ useSnapShot);
+ }
+ if (instanceProperties.getProperty(IDevicePropertiesConstants.saveSnapshot) == null)
+ {
+ instanceProperties
+ .setProperty(IDevicePropertiesConstants.saveSnapshot, useSnapShot);
+ }
+ }
+ }
+
+ public String getSkinId()
+ {
+ return getProperties().getProperty(IDevicePropertiesConstants.skinId);
+ }
+
+ @SuppressWarnings("restriction")
+ public File getSkinPath()
+ {
+ File skinFile = null;
+
+ AvdInfo avdInfo = SdkUtils.getValidVm(getName());
+ if (avdInfo != null)
+ {
+ String skinPath = avdInfo.getProperties().get("skin.path");
+ skinPath = SdkUtils.getCurrentSdk().getSdkLocation() + skinPath;
+ IAndroidTarget target = avdInfo.getTarget();
+ File candidateFile = new File(skinPath);
+ //If path specified on the skin does not exist, try to retrieve it from the target.
+ if (!candidateFile.exists())
+ {
+ candidateFile =
+ SdkUtils.getCurrentSdk().getAvdManager()
+ .getSkinPath(SdkUtils.getSkin(avdInfo), target);
+ }
+ if (!target.isPlatform())
+ {
+ if (!candidateFile.isDirectory())
+ {
+ IAndroidTarget baseTarget = target.getParent();
+ skinPath = getSkinFolderPath(baseTarget);
+ skinFile = new File(skinPath);
+ }
+ else
+ {
+ skinFile = candidateFile;
+ }
+ }
+ else
+ {
+ skinFile = candidateFile;
+ }
+ }
+ return skinFile;
+ }
+
+ private String getSkinFolderPath(IAndroidTarget target)
+ {
+ String vmSkin = getProperties().getProperty(IDevicePropertiesConstants.vmSkin);
+ return target.getLocation() + File.separator + "skins" + File.separator + vmSkin;
+ }
+
+ /**
+ * Get the timeout in milliseconds
+ *
+ * @see com.motorola.studio.android.emulator.logic.IAndroidLogicInstance#getTimeout()
+ */
+ public int getTimeout()
+ {
+ int timeout = 0;
+ String timeoutString = null;
+ try
+ {
+ info("Try to get Timeout property from " + this);
+ Properties instanceProps = getProperties();
+ timeoutString = instanceProps.getProperty(IDevicePropertiesConstants.timeout);
+ timeout = Integer.parseInt(timeoutString) * 1000; //convert to milis
+ }
+ catch (Exception e)
+ {
+ warn("Unnable to parse timeout string:" + timeoutString);
+ timeout = Integer.parseInt(IDevicePropertiesConstants.defaultTimeoutValue) * 1000;
+ }
+
+ return timeout;
+ }
+
+ public IAndroidTarget getAndroidTarget()
+ {
+ IAndroidTarget result = null;
+ AvdInfo avdInfo = SdkUtils.getValidVm(getName());
+ if (avdInfo != null)
+ {
+ result = avdInfo.getTarget();
+ }
+ return result;
+ }
+
+ public String getTarget()
+ {
+ String result = null;
+ IAndroidTarget target = getAndroidTarget();
+ if (target != null)
+ {
+ result = target.getName();
+ }
+ return result;
+ }
+
+ public int getAPILevel()
+ {
+ int result = -1;
+ IAndroidTarget target = getAndroidTarget();
+ if (target != null)
+ {
+ result = target.getVersion().getApiLevel();
+ }
+ return result;
+ }
+
+ public String getCommandLineArguments()
+ {
+ return getProperties().getProperty(IDevicePropertiesConstants.commandline,
+ NativeUIUtils.getDefaultCommandLine());
+ }
+
+ public AbstractStartAndroidEmulatorLogic getStartLogic()
+ {
+ String emuDefinition = getEmulatorDefId();
+ AndroidEmuDefMgr definitionManager = AndroidEmuDefMgr.getInstance();
+ return definitionManager.getStartLogic(emuDefinition);
+ }
+
+ public IInputLogic getInputLogic()
+ {
+ String emuDefinition = getEmulatorDefId();
+ AndroidEmuDefMgr definitionManager = AndroidEmuDefMgr.getInstance();
+ return definitionManager.getInputLogic(emuDefinition, this);
+ }
+
+ public boolean hasDevice()
+ {
+ return (DDMSFacade.getDeviceBySerialNumber(getSerialNumber()) != null);
+ }
+
+ public void changeOrientation(final String parameters)
+ {
+
+ new Thread(new Runnable()
+ {
+
+ public void run()
+ {
+ try
+ {
+ DDMSFacade.execRemoteApp(getSerialNumber(),
+ AndroidLogicUtils.ORIENTATION_BASE_COMMAND + parameters,
+ new NullProgressMonitor());
+ }
+ catch (IOException e)
+ {
+ error("Failed to send the command to change the emulator display orientation to portrait.");
+ }
+
+ }
+ }).start();
+
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.logic.IAndroidLogicInstance#getUserdata()
+ */
+ public File getUserdata()
+ {
+ return SdkUtils.getUserdataFile(getName());
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.logic.IAndroidLogicInstance#getSnapshotOriginalFilePath()
+ */
+ public File getSnapshotOriginalFilePath()
+ {
+ String snapshotFilePath;
+ File snapshotOriginalFile = null;
+ snapshotFilePath =
+ SdkUtils.getSdkToolsPath() + "lib" + File.separator + "emulator" + File.separator
+ + "snapshots.img";
+ snapshotOriginalFile = new File(snapshotFilePath);
+ if (!snapshotOriginalFile.exists())
+ {
+ snapshotOriginalFile = null;
+ }
+ return snapshotOriginalFile;
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.logic.IAndroidLogicInstance#getStateData()
+ */
+ public List<File> getStateData()
+ {
+ return SdkUtils.getStateDataFiles(getName());
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.logic.IAndroidLogicInstance#isClean()
+ */
+ public boolean isClean()
+ {
+ boolean userdataExists = false;
+ File userdataFile = getUserdata();
+ if ((userdataFile != null) && (userdataFile.exists()))
+ {
+ userdataExists = true;
+ }
+
+ return !userdataExists;
+ }
+
+ @Override
+ public String toString()
+ {
+ // Do not use getInstanceIdentifier method here (it is used by several
+ // logs - high exposure - and leads to synchronized methods that may
+ // cause deadlocks).
+ return getName();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance#getProcess()
+ */
+ public Process getProcess()
+ {
+ return process;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance#setProcess(java.lang.Process)
+ */
+ public void setProcess(Process process)
+ {
+ this.process = process;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance#setWindowHandle(int)
+ */
+ public long getWindowHandle()
+ {
+ return windowHandle;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance#getWindowHandle()
+ */
+ public void setWindowHandle(long handle)
+ {
+ windowHandle = handle;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.adt.ISerialNumbered#getDeviceName()
+ */
+ public String getDeviceName()
+ {
+ return getName();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance#getFullName()
+ */
+ public String getFullName()
+ {
+ String suffix = getNameSuffix();
+ if (suffix != null)
+ {
+ return getName() + " (" + suffix + ")";
+ }
+ else
+ {
+ return getName();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getChildren(java.lang.Object)
+ */
+ public Object[] getChildren(Object o)
+ {
+ return new Object[0];
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object)
+ */
+ public ImageDescriptor getImageDescriptor(Object object)
+ {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getLabel(java.lang.Object)
+ */
+ public String getLabel(Object o)
+ {
+ return getName();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getParent(java.lang.Object)
+ */
+ public Object getParent(Object o)
+ {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.adt.ISerialNumbered#getSerialNumber()
+ */
+ public String getSerialNumber()
+ {
+ return DDMSFacade.getSerialNumberByName(getName());
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance#isAvailable()
+ */
+ public boolean isAvailable()
+ {
+ return !getStatus().equals(EmulatorPlugin.STATUS_NOT_AVAILABLE);
+ }
+
+ /* (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.logic.IAndroidLogicInstance#getCommandLineArgumentsAsProperties()
+ */
+ public Properties getCommandLineArgumentsAsProperties()
+ {
+ return StartupOptionsMgt.parseCommandLine(getCommandLineArguments());
+
+ }
+
+ public Composite getComposite()
+ {
+ return composite;
+ }
+
+ public void setComposite(Composite composite)
+ {
+ this.composite = composite;
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/IStartupOptionsConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/IStartupOptionsConstants.java
new file mode 100644
index 0000000..c82796b
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/IStartupOptionsConstants.java
@@ -0,0 +1,155 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.instance.options;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This interface contains constants used for the Startup Options Management
+ *
+ */
+@SuppressWarnings("serial")
+public interface IStartupOptionsConstants
+{
+
+ /*
+ * XML Path
+ */
+ public final String STARTUP_OPTIONS_XML_PATH = "resource/startup_options.xml";
+
+ /*
+ * XML tags
+ */
+ public final String ROOT_TAG = "startupOptions";
+
+ public final String GROUP_TAG = "group";
+
+ public final String GROUP_TAG_ID = "id";
+
+ public final String STARTUP_OPT_TAG = "startupOption";
+
+ public final String STARTUP_OPT_TAG_NAME = "name";
+
+ public final String STARTUP_OPT_TAG_FRIENDLY_NAME = "fName";
+
+ public final String STARTUP_OPT_TAG_TYPE = "type";
+
+ public final String STARTUP_OPT_TAG_TYPE_DETAILS = "typeDetails";
+
+ public final String STARTUP_OPT_TAG_DESCRIPTION = "description";
+
+ public final String PREDEFINED_VALUES_TAG = "values";
+
+ public final String PREDEFINED_VALUE_TAG = "value";
+
+ /*
+ * Startup option value type
+ */
+ public final int TYPE_NONE = 0;
+
+ public final int TYPE_TEXT = 1;
+
+ public final int TYPE_PATH = 2;
+
+ public final int TYPE_NUMBER = 3;
+
+ public final String TYPE_PATH_DIR = "dir";
+
+ public final Map<String, Integer> TYPE_MAP = new HashMap<String, Integer>()
+ {
+ {
+ put("none", TYPE_NONE);
+ put("text", TYPE_TEXT);
+ put("path", TYPE_PATH);
+ put("int", TYPE_NUMBER);
+ }
+
+ };
+
+ /*
+ * Disk images options
+ */
+ public final String DISKIMAGES_GROUP = "Disk Images";
+
+ public final String DISKIMAGES_CACHE = "-cache";
+
+ public final String DISKIMAGES_DATA = "-data";
+
+ public final String DISKIMAGES_IMAGE = "-image";
+
+ public final String DISKIMAGES_INITDATA = "-initdata";
+
+ public final String DISKIMAGES_KERNEL = "-kernel";
+
+ public final String DISKIMAGES_NOCACHE = "-nocache";
+
+ public final String DISKIMAGES_RAMDISK = "-ramdisk";
+
+ public final String DISKIMAGES_SDCARD = "-sdcard";
+
+ public final String DISKIMAGES_SYSTEM = "-system";
+
+ public final String DISKIMAGES_WIPEDATA = "-wipe-data";
+
+ /*
+ * Network options
+ */
+ public final String NETWORK_GROUP = "Network";
+
+ public final String NETWORK_DNS_SERVER = "-dns-server";
+
+ public final String NETWORK_HTTP_PROXY = "-http-proxy";
+
+ public final String NETWORK_NETDELAY = "-netdelay";
+
+ public final String NETWORK_NETFAST = "-netfast";
+
+ public final String NETWORK_NETSPEED = "-netspeed";
+
+ public final String NETWORK_PORT = "-port";
+
+ /*
+ * System options
+ */
+ public final String SYSTEM_GROUP = "System";
+
+ public final String SYSTEM_CPU_DELAY = "-cpu-delay";
+
+ public final String SYSTEM_GPS = "-gps";
+
+ public final String SYSTEM_NO_JNI = "-nojni";
+
+ /*
+ * UI options
+ */
+ public final String UI_GROUP = "UI";
+
+ public final String UI_DPI_DEVICE = "-dpi-device";
+
+ public final String SCALE = "-scale";
+
+ public final String NO_BOOT_AIM = "-no-boot-anim";
+
+ public final String NO_SKIN = "-no-skin";
+
+ /*
+ * Other options
+ */
+ public final String OTHERS_GROUP = "Others";
+
+ public final String OTHERS_OTHER = "other";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOption.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOption.java
new file mode 100644
index 0000000..12a15c7
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOption.java
@@ -0,0 +1,307 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.instance.options;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * Bean that represents an startup option
+ *
+ */
+public class StartupOption
+{
+
+ // Checked status (whether the startup options is being used or not)
+ private boolean checked;
+
+ // Widget that represents the checked status in the UI
+ private Widget checkedWidget;
+
+ // Startup option name
+ private String name;
+
+ // Startup option user-friendly name
+ private String userFriendlyName;
+
+ // Startup option description (user-friendly description)
+ private String description;
+
+ // Startup option type (which type of values that the startup option accepts)
+ private int type;
+
+ // Startup option type details (details of the values that the startup option accepts)
+ private String typeDetails;
+
+ // Startup option value (startup option configured value)
+ private String value;
+
+ // Widget that represents the startup option value in the UI
+ private Widget valueWidget;
+
+ // Startup option predefined values (list of values the startup option accepts)
+ private List<String> preDefinedValues;
+
+ /**
+ * Constructor
+ *
+ * @param name
+ * @param type
+ */
+ public StartupOption(String name, int type)
+ {
+ this.checked = false;
+ this.name = name;
+ this.type = type;
+ this.value = "";
+ this.preDefinedValues = new ArrayList<String>();
+ }
+
+ /**
+ * Get startup option name
+ *
+ * @return startup option name
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Set startup option name
+ *
+ * @param name startup option name
+ */
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * Get startup option user-friendly name
+ *
+ * @return
+ */
+ public String getUserFriendlyName()
+ {
+ return userFriendlyName;
+ }
+
+ /**
+ * Set startup option user-friendly name
+ *
+ * @param userFriendlyName
+ */
+ public void setUserFriendlyName(String userFriendlyName)
+ {
+ this.userFriendlyName = userFriendlyName;
+ }
+
+ /**
+ * Get startup option type
+ *
+ * @return startup option type
+ */
+ public int getType()
+ {
+ return type;
+ }
+
+ /**
+ * Set startup option type
+ *
+ * @param type startup option type
+ */
+ public void setType(int type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * Get startup option value
+ *
+ * @return startup option value
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Set startup option value
+ *
+ * @param value startup option value
+ */
+ public void setValue(String value)
+ {
+ this.value = value;
+ }
+
+ /**
+ * Get startup option pre-defined values
+ *
+ * @return startup option pre-defined values
+ */
+ public List<String> getPreDefinedValues()
+ {
+ return preDefinedValues;
+ }
+
+ /**
+ * Set startup option pre-defined values
+ *
+ * @param preDefinedValues startup option pre-defined values
+ */
+ public void setPreDefinedValues(List<String> preDefinedValues)
+ {
+ this.preDefinedValues = preDefinedValues;
+ }
+
+ /**
+ * Check if the startup option is being used
+ *
+ * @return true if the startup option is being used, false otherwise
+ */
+ public boolean isChecked()
+ {
+ return checked;
+ }
+
+ /**
+ * Set that the startup option is being used or not
+ *
+ * @param checked true if the startup option is being used, false otherwise
+ */
+ public void setChecked(boolean checked)
+ {
+ this.checked = checked;
+ }
+
+ /**
+ * Get startup option description
+ *
+ * @return startup option description
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * Set startup option description
+ *
+ * @param description startup option description
+ */
+ public void setDescription(String description)
+ {
+ this.description = description;
+ }
+
+ /**
+ * Get startup option type details
+ *
+ * @return startup option type details
+ */
+ public String getTypeDetails()
+ {
+ return typeDetails;
+ }
+
+ /**
+ * Get startup option type details
+ *
+ * @param typeDetails valuable information for validating if the value assigned is correct
+ */
+ public void setTypeDetails(String typeDetails)
+ {
+ this.typeDetails = typeDetails;
+ }
+
+ /**
+ * Get the widget that represents the checked status in the UI
+ *
+ * @return widget that represents the checked status in the UI
+ */
+ public Widget getCheckedWidget()
+ {
+ return checkedWidget;
+ }
+
+ /**
+ * Set the widget that represents the checked status in the UI
+ *
+ * @param checkedWidget widget that represents the checked status in the UI
+ */
+ public void setCheckedWidget(Widget checkedWidget)
+ {
+ this.checkedWidget = checkedWidget;
+ }
+
+ /**
+ * Get the widget that represents the startup option value in the UI
+ *
+ * @return widget that represents the startup option value in the UI
+ */
+ public Widget getValueWidget()
+ {
+ return valueWidget;
+ }
+
+ /**
+ * Set the widget that represents the startup option value in the UI
+ *
+ * @param valueWidget widget that represents the startup option value in the UI
+ */
+ public void setValueWidget(Widget valueWidget)
+ {
+ this.valueWidget = valueWidget;
+ }
+
+ /**
+ * Update the widgets that represent this startup options in the UI
+ * by changing their state to match the current values for checked and value
+ */
+ public void updateUI()
+ {
+ if (checkedWidget != null && !checkedWidget.isDisposed())
+ {
+ ((Button) this.checkedWidget).setSelection(this.checked);
+ }
+ if (valueWidget != null && !checkedWidget.isDisposed())
+ {
+ if (this.valueWidget instanceof Text)
+ {
+ ((Text) this.valueWidget).setText(this.value);
+ }
+ else if (this.valueWidget instanceof Combo)
+ {
+ if ((this.value == null) || (this.value.equals("")))
+ {
+ ((Combo) this.valueWidget).deselectAll();
+ }
+ else
+ {
+ ((Combo) this.valueWidget).select(getPreDefinedValues().indexOf(this.value));
+ }
+ }
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOptionsGroup.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOptionsGroup.java
new file mode 100644
index 0000000..1348318
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOptionsGroup.java
@@ -0,0 +1,106 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.instance.options;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Bean that represents an startup options group
+ *
+ */
+public class StartupOptionsGroup
+{
+ // Group ID
+ private String id;
+
+ // Group Title (user-friendly title)
+ private String title;
+
+ // Startup options (list of the startup options in this group)
+ private List<StartupOption> startupOptions = new ArrayList<StartupOption>();
+
+ /**
+ * Constructor
+ *
+ * @param id
+ */
+ public StartupOptionsGroup(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * Get startup option group ID
+ *
+ * @return startup option group ID
+ */
+ public String getId()
+ {
+ return id;
+ }
+
+ /**
+ * Set startup option group ID
+ *
+ * @param id startup option group ID
+ */
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ /**
+ * Get the startup options in this group
+ *
+ * @return startup options in this group
+ */
+ public List<StartupOption> getStartupOptions()
+ {
+ return startupOptions;
+ }
+
+ /**
+ * Set the startup options in this group
+ *
+ * @param startupOptions startup options in this group
+ */
+ public void setStartupOptions(List<StartupOption> startupOptions)
+ {
+ this.startupOptions = startupOptions;
+ }
+
+ /**
+ * Get startup option group title
+ *
+ * @return startup option group title
+ */
+ public String getTitle()
+ {
+ return title;
+ }
+
+ /**
+ * Set startup option group title
+ *
+ * @param title startup option group title
+ */
+ public void setTitle(String title)
+ {
+ this.title = title;
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOptionsMgt.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOptionsMgt.java
new file mode 100644
index 0000000..bd5710d
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/instance/options/StartupOptionsMgt.java
@@ -0,0 +1,785 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.instance.options;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * This class provides methods to manage startup options
+ *
+ */
+public class StartupOptionsMgt implements IStartupOptionsConstants
+{
+
+ /**
+ * List of all startup options groups (the startup options themselves will
+ * be accessed through the use of a method in the group object that returns
+ * the startup options in that group)
+ */
+ private static List<StartupOptionsGroup> startupOptionsGroupsList = null;
+
+ /**
+ * List of all startup options, indexed by their names, for fast access
+ */
+ private static Map<String, StartupOption> startupOptionsMap =
+ new HashMap<String, StartupOption>();
+
+ /*
+ * Load the startup options / groups list
+ */
+ static
+ {
+ load();
+ }
+
+ /**
+ * Get the startup options groups list
+ *
+ * @return startup options groups list
+ */
+ public static List<StartupOptionsGroup> getStartupOptionsGroupsList()
+ {
+ return startupOptionsGroupsList;
+ }
+
+ /**
+ * Read all groups and startup options available for editing from a XML
+ * and stores the information in the correspondent beans
+ */
+ public static void load()
+ {
+
+ try
+ {
+ // Clear startup options groups list
+ startupOptionsGroupsList = new ArrayList<StartupOptionsGroup>();
+
+ // Define XML path
+ InputStream xmlStream =
+ EmulatorPlugin.getDefault().getBundle().getEntry(STARTUP_OPTIONS_XML_PATH)
+ .openStream();
+
+ // Load XML
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.parse(xmlStream);
+
+ /*
+ * Iterate through Startup Groups
+ */
+ Element rootNode = document.getDocumentElement();
+ NodeList startupOptionsGroups = rootNode.getElementsByTagName(GROUP_TAG);
+ for (int i = 0; i < startupOptionsGroups.getLength(); i++)
+ {
+ /*
+ * Create group
+ */
+ Element group = (Element) startupOptionsGroups.item(i);
+
+ String strKey = group.getAttributeNode(GROUP_TAG_ID).getNodeValue();
+ strKey =
+ Platform.getResourceString(EmulatorPlugin.getDefault().getBundle(), strKey);
+
+ StartupOptionsGroup startupOptionsGroup = new StartupOptionsGroup(strKey);
+ startupOptionsGroup.setTitle(startupOptionsGroup.getId());
+
+ /*
+ * Iterate through Startup Options in this group
+ */
+ NodeList startupOptions = group.getElementsByTagName(STARTUP_OPT_TAG);
+ startupOptionsGroup.setStartupOptions(new ArrayList<StartupOption>()); // clear startup options
+ for (int j = 0; j < startupOptions.getLength(); j++)
+ {
+ /*
+ * Create startup option
+ */
+ Element option = (Element) startupOptions.item(j);
+ StartupOption startupOption =
+ new StartupOption(option.getAttributeNode(STARTUP_OPT_TAG_NAME)
+ .getNodeValue(), getStartupOptionType(option.getAttributeNode(
+ STARTUP_OPT_TAG_TYPE).getNodeValue())); // name and type
+ strKey = option.getAttributeNode(STARTUP_OPT_TAG_FRIENDLY_NAME).getNodeValue();
+
+ strKey =
+ Platform.getResourceString(EmulatorPlugin.getDefault().getBundle(),
+ strKey);
+
+ startupOption.setUserFriendlyName(strKey); // friendly name
+
+ strKey =
+ option.getElementsByTagName(STARTUP_OPT_TAG_DESCRIPTION).item(0)
+ .getTextContent();
+
+ strKey =
+ Platform.getResourceString(EmulatorPlugin.getDefault().getBundle(),
+ strKey);
+
+ startupOption.setDescription(strKey); // description
+
+ if (option.getAttributeNode(STARTUP_OPT_TAG_TYPE_DETAILS) != null)
+ {
+ startupOption.setTypeDetails(option.getAttributeNode(
+ STARTUP_OPT_TAG_TYPE_DETAILS).getNodeValue()); // type details
+ }
+ // Iterate through startup option pre-defined values, if any
+ NodeList preDefinedValuesContainer =
+ option.getElementsByTagName(PREDEFINED_VALUES_TAG);
+ startupOption.setPreDefinedValues(new ArrayList<String>()); // clear pre-defined values
+ if (preDefinedValuesContainer.getLength() > 0)
+ {
+ NodeList preDefinedValues =
+ ((Element) preDefinedValuesContainer.item(0))
+ .getElementsByTagName(PREDEFINED_VALUE_TAG);
+ for (int k = 0; k < preDefinedValues.getLength(); k++)
+ {
+ // Add pre-defined values to the option
+ Element preDefinedValue = (Element) preDefinedValues.item(k);
+ startupOption.getPreDefinedValues().add(
+ preDefinedValue.getTextContent());
+ }
+ }
+
+ /*
+ * Add startup options to the group
+ */
+ startupOptionsGroup.getStartupOptions().add(startupOption);
+
+ startupOptionsMap.put(startupOption.getName(), startupOption);
+
+ }
+
+ /*
+ * Add groups to the groups list
+ */
+ startupOptionsGroupsList.add(startupOptionsGroup);
+ }
+
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Failed to load startup options");
+ }
+
+ }
+
+ /**
+ * Validate the values assigned for the startup options marked as checked (the ones that are
+ * being used), according to its type and type details.
+ *
+ * @return Status object with the result of the validation
+ */
+ public static Status validate()
+ {
+ Status status = (Status) Status.OK_STATUS;
+ String msg = null;
+
+ /*
+ * Iterate through Startup Groups
+ */
+ for (StartupOptionsGroup group : getStartupOptionsGroupsList())
+ {
+ /*
+ * Iterate through Startup Options in this group
+ */
+ for (StartupOption startupOption : group.getStartupOptions())
+ {
+ /*
+ * Check if the Startup Option is checked
+ */
+ if (startupOption.isChecked() && (status.isOK()))
+ {
+
+ String name = startupOption.getName(); // startup option name
+ String ufname = startupOption.getUserFriendlyName(); // user-friendly startup option name
+ String value = startupOption.getValue(); // startup option value
+ String typeDetails = startupOption.getTypeDetails(); // startup option type detail
+
+ /*
+ * General validation: no quotes in values
+ */
+ if ((!startupOption.getName().equals(OTHERS_OTHER))
+ && (value.indexOf("\"") >= 0))
+ {
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_NoQuotes,
+ ufname);
+ }
+ else
+ {
+
+ /*
+ * Call the appropriate validation method
+ */
+ switch (startupOption.getType())
+ {
+ case TYPE_TEXT:
+ msg = validateTextField(name, ufname, value, typeDetails);
+ break;
+
+ case TYPE_NUMBER:
+ msg = validadeNumberField(name, ufname, value, typeDetails);
+ break;
+
+ case TYPE_PATH:
+ msg = validadePathField(name, ufname, value, typeDetails);
+ break;
+ }
+ }
+
+ /*
+ * If some validation has failed, return with an error message
+ */
+ if (msg != null)
+ {
+ status = new Status(Status.ERROR, EmulatorPlugin.PLUGIN_ID, msg);
+ break;
+ }
+
+ }
+ }
+ }
+
+ return status;
+
+ }
+
+ /**
+ * Validate the startup option value for an startup option of "text" type
+ *
+ * @param name the startup option name
+ * @param ufname the user-friendly startup option name
+ * @param value the current assigned value for the startup option
+ * @param typeDetails any special requirements that the assigned value must be match
+ * @return null if the value assigned for the startup option is a valid one or an error message otherwise
+ */
+ private static String validateTextField(String name, String ufName, String value,
+ String typeDetails)
+ {
+ String msg = null;
+
+ // Check if the value is blank
+ if ((value == null) || (value.equals("")))
+ {
+ msg = NLS.bind(EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_TextBlank, ufName);
+ }
+
+ return msg;
+
+ }
+
+ /**
+ * Validate the startup option value for an startup option of "number" type
+ *
+ * @param name the startup option name
+ * @param ufname the user-friendly startup option name
+ * @param value the current assigned value for the startup option
+ * @param typeDetails any special requirements that the assigned value must be match
+ * @return null if the value assigned for the startup option is a valid one or an error message otherwise
+ */
+ private static String validadeNumberField(String name, String ufName, String value,
+ String typeDetails)
+ {
+ String msg = null;
+
+ // Check if the value is blank
+ if ((value == null) || (value.equals("")))
+ {
+ msg =
+ NLS.bind(EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_NumberRequired,
+ ufName);
+ }
+ else
+ {
+
+ try
+ {
+ /*
+ * Check if it's an Integer.
+ * If it's not, an exception will be thrown
+ */
+ int intValue = Integer.parseInt(value);
+
+ /*
+ * Check if it's positive
+ */
+ if (intValue < 0)
+ {
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_NumberMustBePositiveInteger,
+ ufName);
+ }
+ else
+ {
+
+ /*
+ * Check if the value is in the correct range
+ */
+ if (typeDetails != null)
+ {
+ String[] valueRange = typeDetails.split(";");
+ if ((intValue < Integer.parseInt(valueRange[0]))
+ || (intValue > Integer.parseInt(valueRange[1])))
+ {
+ // the value is not in the correct range
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_NumberIntRange,
+ new String[]
+ {
+ ufName, valueRange[0], valueRange[1]
+ });
+ }
+ }
+ }
+
+ }
+ catch (NumberFormatException ex)
+ {
+ // it's not a number
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_NumberMustBeInteger,
+ ufName);
+ }
+ }
+
+ return msg;
+
+ }
+
+ /**
+ * Validate the startup option value for an startup option of "path" type
+ *
+ * @param name the startup option name
+ * @param ufname the user-friendly startup option name
+ * @param value the current assigned value for the startup option
+ * @param typeDetails any special requirements that the assigned value must be match
+ * @return null if the value assigned for the startup option is a valid one or an error message otherwise
+ */
+ private static String validadePathField(String name, String ufName, String value,
+ String typeDetails)
+ {
+ String msg = null;
+
+ if ((value == null) || (value.equals("")))
+ {
+ msg = NLS.bind(EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathRequired, ufName);
+ }
+ else
+ {
+
+ File file = new File(value);
+
+ /*
+ * Validate folder
+ */
+ if (typeDetails.equals(TYPE_PATH_DIR))
+ {
+ /*
+ * Check if the path exists
+ */
+ if (!file.exists())
+ {
+ // the folder doesn't exist
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathDirNotExist,
+ ufName);
+ }
+ else
+ {
+ if (file.isFile())
+ {
+ // it's not a folder
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathMustBeDir,
+ ufName);
+ }
+ }
+ }
+ /*
+ * Validate file
+ */
+ else
+ {
+ if (!file.exists())
+ {
+ // the file doesn't exist
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathFileNotExist,
+ ufName);
+ }
+ else
+ {
+ // it's not a file
+ if (file.isDirectory())
+ {
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathMustBeFile,
+ ufName);
+ }
+ // it doesn't have the correct extension
+ else
+ {
+ if (!typeDetails.equals("." + (new Path(value)).getFileExtension()))
+ {
+ msg =
+ NLS.bind(
+ EmulatorNLS.ERR_PropertiesMainComposite_StartupOpt_PathIncorrectFileType,
+ new String[]
+ {
+ ufName, typeDetails
+ });
+ }
+ }
+ }
+ }
+ }
+ return msg;
+
+ }
+
+ /**
+ * Generates the list of parameters that shall to be sent to the Emulator
+ *
+ * @return the list of parameters that shall to be sent to the Emulator
+ */
+ public static String getParamList()
+ {
+ String paramList = "";
+
+ /*
+ * Iterate through Startup Groups
+ */
+ for (StartupOptionsGroup group : getStartupOptionsGroupsList())
+ {
+ /*
+ * Iterate through Startup Options in this group
+ */
+ int startupOptionType;
+ for (StartupOption startupOption : group.getStartupOptions())
+ {
+ startupOptionType = startupOption.getType();
+ if (startupOption.isChecked()) // check if the startup option is being used
+ {
+ if (startupOptionType == TYPE_NONE)
+ {
+ paramList += ((paramList.equals("")) ? "" : " ") + startupOption.getName();
+ }
+ else
+ {
+ if ((startupOption.getName().equals(OTHERS_OTHER)))
+ {
+
+ paramList +=
+ ((paramList.equals("")) ? "" : " ") + startupOption.getValue();
+
+ }
+ else
+ {
+ String value = startupOption.getValue();
+
+ if (Platform.getOS().equals(Platform.OS_WIN32))
+ {
+ if (value.contains(" "))
+ {
+ value = "\"" + value + "\"";
+ }
+ }
+ else
+ {
+ if (value.contains("\\"))
+ {
+ value = value.replace("\\", "\\\\");
+ }
+
+ if (value.contains(" "))
+ {
+ value = value.replace(" ", "\\ ");
+ }
+ }
+
+ paramList +=
+ ((paramList.equals("")) ? "" : " ") + startupOption.getName()
+ + (value.trim().length() > 0 ? " " + value : "");
+ }
+ }
+ }
+ }
+ }
+
+ return paramList;
+
+ }
+
+ /**
+ * Load values from a Properties object
+ *
+ * @param properties properties object containing the values that must be loaded into de model
+ */
+ private static void loadValues(Properties properties)
+ {
+ /*
+ * Iterate through Startup Groups
+ */
+ for (StartupOptionsGroup group : getStartupOptionsGroupsList())
+ {
+ /*
+ * Iterate through Startup Options in this group
+ */
+ String soValue = "";
+ for (StartupOption startupOption : group.getStartupOptions())
+ {
+ soValue = properties.getProperty(startupOption.getName());
+ if (soValue != null)
+ {
+ startupOption.setChecked(true);
+ startupOption.setValue(soValue);
+ }
+ else
+ {
+ startupOption.setChecked(false);
+ startupOption.setValue("");
+ }
+ startupOption.updateUI();
+ }
+ }
+
+ }
+
+ /**
+ * Create a properties object with the information contained in a command line
+ *
+ * @param commandLine the command line used to start the emulator
+ * @return properties object with the information contained in a command line
+ */
+ public static Properties parseCommandLine(String commandLine)
+ {
+ Properties properties = new Properties();
+
+ if (!commandLine.equals(""))
+ {
+
+ /*
+ * Iterate through Startup Groups
+ */
+ for (StartupOptionsGroup group : getStartupOptionsGroupsList())
+ {
+ /*
+ * Iterate through Startup Options in this group
+ */
+ String soName, soValue = "";
+ int soType, shift = 0;
+ for (StartupOption startupOption : group.getStartupOptions())
+ {
+ soName = startupOption.getName();
+ soType = startupOption.getType();
+ if (commandLine.startsWith(soName))
+ {
+ if (soType == TYPE_NONE)
+ {
+ soValue = new Boolean(true).toString();
+ shift = soName.length() + 1;
+ }
+ else
+ {
+ commandLine =
+ commandLine
+ .substring(soName.length() + 1, commandLine.length());
+ //int endValueIndex = commandLine.indexOf("\"");
+ ParameterBean param = getNextParameterValue(commandLine);
+ soValue = param.getValue();
+ shift = param.getLastPosition() + 1;
+ }
+
+ properties.put(startupOption.getName(), soValue);
+
+ if (shift < commandLine.length() - 1)
+ {
+ commandLine = commandLine.substring(shift, commandLine.length());
+ }
+ else
+ {
+ commandLine = "";
+ }
+ }
+ }
+ }
+
+ if (!commandLine.equals(""))
+ {
+ properties.put(OTHERS_OTHER, commandLine);
+ }
+ }
+
+ return properties;
+
+ }
+
+ /**
+ * Load values from a command line
+ *
+ * @param commandLine the command line used to start the emulator
+ */
+ public static void loadFromCommandLine(String commandLine)
+ {
+ loadValues(parseCommandLine(commandLine));
+ }
+
+ /**
+ * Convert the type of the startup option from a string
+ * to a number
+ *
+ * @param type string that represents the type
+ * @return number that represents the type
+ */
+ private static int getStartupOptionType(String type)
+ {
+ return (TYPE_MAP.get(type)).intValue();
+ }
+
+ /**
+ * Parses the next parameter value on a command line
+ *
+ * @param commandLine the command line
+ *
+ * @return a bean containing the parameter value and the last position looked at
+ * the command line
+ */
+ private static ParameterBean getNextParameterValue(String commandLine)
+ {
+ boolean isWin32 = Platform.getOS().equals(Platform.OS_WIN32);
+ boolean escaped = false;
+ boolean quoted = false;
+
+ char c;
+ String value = "";
+ int i;
+
+ for (i = 0; i < commandLine.length(); i++)
+ {
+ c = commandLine.charAt(i);
+
+ if (escaped)
+ {
+ value += c;
+ escaped = false;
+ }
+ else if (c == '\\' && !isWin32)
+ {
+ escaped = true;
+ }
+ else if (c == '"' && isWin32)
+ {
+ if (value.length() == 0)
+ {
+ quoted = true;
+ }
+ else if (quoted)
+ {
+ break;
+ }
+ else
+ {
+ value += c;
+ }
+ }
+ else if ((c == ' ') && (!quoted))
+ {
+ break;
+ }
+ else
+ {
+ value += c;
+ }
+ }
+
+ return new ParameterBean(value, ((quoted) ? i + 1 : i));
+ }
+
+ /**
+ * Bean used to identify a parameter value when parsing the
+ * startup options
+ *
+ */
+ private static class ParameterBean
+ {
+ private final String value;
+
+ private final int lastPosition;
+
+ /**
+ * Constructor
+ *
+ * @param value The parameter value
+ * @param lastPosition The last position looked at the command line before stopping
+ * the parse operation
+ */
+ public ParameterBean(String value, int lastPosition)
+ {
+ this.value = value;
+ this.lastPosition = lastPosition;
+ }
+
+ /**
+ * Retrieves the parameter value
+ *
+ * @return the parameter value
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Retrieves the last position looked at the command line before stopping
+ * the parse operation
+ *
+ * @return the last position looked at the command line before stopping
+ * the parse operation
+ */
+ public int getLastPosition()
+ {
+ return lastPosition;
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/refresh/InstancesListRefresh.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/refresh/InstancesListRefresh.java
new file mode 100644
index 0000000..23295e9
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/refresh/InstancesListRefresh.java
@@ -0,0 +1,184 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.refresh;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.Collection;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.DevicePlugin;
+import org.eclipse.sequoyah.device.framework.factory.DeviceTypeRegistry;
+import org.eclipse.sequoyah.device.framework.manager.InstanceManager;
+import org.eclipse.sequoyah.device.framework.model.IDeviceType;
+
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.device.AndroidDeviceUtils;
+import com.motorola.studio.android.emulator.device.instance.AndroidDevInstBuilder;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+
+/**
+ * This class is responsible for refreshing the TML Instances List It checks if
+ * the user has created more Android VMs by himself
+ *
+ */
+public class InstancesListRefresh
+{
+
+ /**
+ * If the number of Android VMs is different from the number of TML
+ * Instances, the TML Instances list is updated
+ */
+ public static synchronized void refresh()
+ {
+
+ SdkUtils.reloadAvds();
+
+ DeviceFrameworkManager devFramework = DeviceFrameworkManager.getInstance();
+
+ if (SdkUtils.getCurrentSdk() != null)
+ {
+ final Collection<String> vmInstances = SdkUtils.getAllVmNames();
+ final Collection<String> validVmInstances = SdkUtils.getAllValidVmNames();
+ final Collection<String> emulatorInstances = devFramework.getAllInstanceNames();
+
+ createAndUpdateEmulatorInstances(vmInstances, validVmInstances, emulatorInstances);
+ }
+ }
+
+ /**
+ * Creates Emulator instances to represent every VM available in the system.
+ * @param validVmInstances
+ **/
+ public static void createAndUpdateEmulatorInstances(Collection<String> vmInstances,
+ Collection<String> validVmInstances,
+ Collection<String> emulatorInstances)
+ {
+
+ IDeviceType device =
+ DeviceTypeRegistry.getInstance().getDeviceTypeById(EmulatorPlugin.DEVICE_ID);
+
+ for (String instanceName : vmInstances)
+ {
+ /*
+ * In case the is no TmL instances for a given VM, create the TmL
+ * Instance
+ */
+ if (!emulatorInstances.contains(instanceName))
+ {
+
+ Properties instanceProperties = new Properties();
+
+ AndroidDeviceInstance.populateWithVMInfo(instanceName, instanceProperties);
+
+ AndroidDeviceInstance.populateWithDefaultProperties(instanceProperties);
+
+ AndroidDevInstBuilder projectBuilder =
+ new AndroidDevInstBuilder(instanceName, instanceProperties);
+
+ try
+ {
+ InstanceManager
+ .createProject(device, projectBuilder, new NullProgressMonitor());
+ }
+ catch (SequoyahException e)
+ {
+ error("There was an error while creating an emulator instance: " + instanceName
+ + ". Message: " + e.getMessage());
+ }
+
+ refreshStatus(validVmInstances, instanceName);
+
+ info("Added instance " + instanceName + " using default emulator definitions ");
+ }
+
+ }
+
+ for (String emulatorInstance : emulatorInstances)
+ {
+ refreshStatus(validVmInstances, emulatorInstance);
+ }
+
+ }
+
+ public static void refreshStatus(Collection<String> vmInstances, String instanceName)
+ {
+ /*
+ * Refresh status
+ */
+ IAndroidEmulatorInstance instance =
+ DeviceFrameworkManager.getInstance().getInstanceByName(instanceName);
+
+ AndroidDeviceInstance androidDeviceInstance = (AndroidDeviceInstance) instance;
+
+ AndroidDeviceInstance.populateWithVMInfo(androidDeviceInstance.getName(),
+ androidDeviceInstance.getProperties());
+
+ String currentStatus = androidDeviceInstance.getStatus();
+
+ if (androidDeviceInstance.hasDevice())
+ {
+ if ((androidDeviceInstance.getStatus().equals(EmulatorPlugin.STATUS_NOT_AVAILABLE))
+ || (androidDeviceInstance.getStatus().equals(DevicePlugin.SEQUOYAH_STATUS_OFF)))
+ {
+ if (!EmulatorPlugin.STATUS_OFFLINE_NO_DATA.equals(currentStatus))
+ {
+ androidDeviceInstance.setNameSuffix(null);
+ androidDeviceInstance.setStatus(EmulatorPlugin.STATUS_OFFLINE_NO_DATA);
+ }
+ }
+ AndroidDeviceUtils.fireDummyStartTransition(androidDeviceInstance,
+ androidDeviceInstance.getSerialNumber());
+ }
+ else
+ {
+
+ if (vmInstances.contains(androidDeviceInstance.getName()))
+ {
+ if (androidDeviceInstance.isClean())
+ {
+ if (!EmulatorPlugin.STATUS_OFFLINE_NO_DATA.equals(currentStatus))
+ {
+ androidDeviceInstance.setNameSuffix(null);
+ androidDeviceInstance.setStatus(EmulatorPlugin.STATUS_OFFLINE_NO_DATA);
+ }
+ }
+ else
+ {
+ if (!EmulatorPlugin.STATUS_OFFLINE.equals(currentStatus))
+ {
+ androidDeviceInstance.setNameSuffix(null);
+ androidDeviceInstance.setStatus(EmulatorPlugin.STATUS_OFFLINE);
+ }
+ }
+ }
+ else
+ {
+ if (!EmulatorPlugin.STATUS_NOT_AVAILABLE.equals(currentStatus))
+ {
+ androidDeviceInstance.setNameSuffix(null);
+ androidDeviceInstance.setStatus(EmulatorPlugin.STATUS_NOT_AVAILABLE);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/refresh/InstancesListRefreshHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/refresh/InstancesListRefreshHandler.java
new file mode 100644
index 0000000..3d68c4b
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/refresh/InstancesListRefreshHandler.java
@@ -0,0 +1,39 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.refresh;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+/**
+ * Class responsible for handling refresh actions
+ *
+ */
+public class InstancesListRefreshHandler extends AbstractHandler
+{
+
+ /**
+ * Call the method responsible for refreshing the TML Instances List
+ *
+ * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent arg0) throws ExecutionException
+ {
+ InstancesListRefresh.refresh();
+ return null;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/sync/DeviceViewsSync.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/sync/DeviceViewsSync.java
new file mode 100644
index 0000000..ec7c49e
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/sync/DeviceViewsSync.java
@@ -0,0 +1,326 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.sync;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.eclipse.sequoyah.device.framework.factory.InstanceRegistry;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.ui.view.InstanceMgtView;
+import org.eclipse.sequoyah.device.framework.ui.view.model.InstanceSelectionChangeEvent;
+import org.eclipse.sequoyah.device.framework.ui.view.model.InstanceSelectionChangeListener;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+
+import com.android.ddmlib.Client;
+import com.android.ddmlib.IDevice;
+import com.android.ide.eclipse.ddms.DdmsPlugin;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+
+public class DeviceViewsSync
+{
+
+ /**
+ * DeviceViewsSync unique instance
+ */
+ private static DeviceViewsSync instance = null;
+
+ /**
+ * Views
+ */
+ public static final int EMULATOR_VIEW = 0; // Emulator View
+
+ public static final int DEVICE_VIEW = 1; // Device Management View
+
+ public static final int DDMS_VIEW = 2; // DDMS Device View
+
+ /**
+ * Methods used to update the Views
+ */
+ private Method[] syncMethods = null;
+
+ /**
+ * During the synchronization, it stores the instance
+ * that shall be set to avoid loops
+ */
+ private static String syncInstance = null;
+
+ /**
+ * Singleton
+ *
+ * @return DeviceViewsSync
+ */
+ public static DeviceViewsSync getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new DeviceViewsSync();
+ }
+ return instance;
+ }
+
+ /*
+ * Constructor
+ *
+ * Define the synchronization methods
+ * Define the methods that retrieve the current selection in a View
+ */
+ @SuppressWarnings("rawtypes")
+ private DeviceViewsSync()
+ {
+
+ try
+ {
+
+ /*
+ * Register methods that update each view
+ */
+ Class parameterTypes[] = new Class[1];
+ parameterTypes[0] = String.class;
+
+ syncMethods = new Method[3];
+
+ syncMethods[EMULATOR_VIEW] =
+ this.getClass().getDeclaredMethod("syncEmulatorView", parameterTypes);
+ syncMethods[DEVICE_VIEW] =
+ this.getClass().getDeclaredMethod("syncDeviceView", parameterTypes);
+ syncMethods[DDMS_VIEW] =
+ this.getClass().getDeclaredMethod("syncDDMSView", parameterTypes);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Could not add syncronization method: " + e.getMessage());
+ }
+
+ }
+
+ /**
+ * Add listeners to events that must initiate the synchronization procedures
+ *
+ * #1) Emulator View
+ *
+ * #2) Device Management View
+ *
+ * #3) DDMS Device View
+ */
+ public void initialize()
+ {
+
+ /*
+ * Synchronization #1
+ * Add listener to Emulator View tab switch event
+ */
+ AbstractAndroidView.addTabSwitchListener(new Listener()
+ {
+ @Override
+ public void handleEvent(Event event)
+ {
+
+ IAndroidEmulatorInstance activeInstance = AbstractAndroidView.getActiveInstance();
+ if (activeInstance != null)
+ {
+ String selectedInstanceName = activeInstance.getName();
+ if ((selectedInstanceName != null)
+ && (!selectedInstanceName.equals(syncInstance)))
+ {
+ sync(EMULATOR_VIEW, selectedInstanceName);
+ }
+ }
+
+ }
+ });
+
+ /*
+ * Synchronization #2
+ */
+ InstanceMgtView.addInstanceSelectionChangeListener(new InstanceSelectionChangeListener()
+ {
+ @Override
+ public void instanceSelectionChanged(InstanceSelectionChangeEvent event)
+ {
+
+ IInstance instance = event.getInstance();
+ if ((instance != null)
+ && (EmulatorPlugin.STATUS_ONLINE_ID.equals(instance.getStatus())))
+ {
+ String selectedInstanceName = instance.getName();
+ if ((selectedInstanceName != null)
+ && (!selectedInstanceName.equals(syncInstance)))
+ {
+ sync(DEVICE_VIEW, selectedInstanceName);
+ }
+ }
+
+ }
+ });
+
+ /*
+ * Synchronization #3
+ */
+ DdmsPlugin.getDefault().addSelectionListener(new DdmsPlugin.ISelectionListener()
+ {
+ @Override
+ public void selectionChanged(Client client)
+ {
+ // none
+ }
+
+ @Override
+ public void selectionChanged(IDevice device)
+ {
+
+ if (device != null)
+ {
+ String selectedInstanceName = device.getAvdName();
+ if ((selectedInstanceName != null)
+ && (!selectedInstanceName.equals(syncInstance)))
+ {
+ sync(DDMS_VIEW, selectedInstanceName);
+ }
+ }
+
+ }
+ });
+ }
+
+ /*
+ * Run the synchronization procedures
+ *
+ * @param fireSyncView the View that has been changed and requires others to synchronize
+ * @param instanceName the Device Instance name
+ */
+ private void sync(Integer fireSyncView, String instanceName)
+ {
+ syncInstance = instanceName;
+
+ Object arglist[] = new Object[1];
+ arglist[0] = instanceName;
+
+ for (int i = 0; i < syncMethods.length; i++)
+ {
+ if (i != fireSyncView)
+ {
+ try
+ {
+ syncMethods[i].invoke(this, arglist);
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Could not call syncronization method for " + i + " : "
+ + e.getMessage());
+ }
+ }
+ }
+
+ syncInstance = null;
+
+ }
+
+ /*
+ * Synchronize the Emulator View by setting the selected instance
+ *
+ * @param instanceName the Device Instance name
+ */
+ @SuppressWarnings("unused")
+ private void syncEmulatorView(String instanceName)
+ {
+ try
+ {
+ IAndroidEmulatorInstance emulatorInstance =
+ DeviceFrameworkManager.getInstance().getInstanceByName(instanceName);
+ if (emulatorInstance != null)
+ {
+ AbstractAndroidView.setInstance(emulatorInstance);
+ }
+ else
+ {
+ StudioLogger.warn("Could not synchronize with Emulator View: " + instanceName
+ + " not in DeviceFrameworkManager model");
+ }
+
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Could not synchronize with Emulator View: " + e.getMessage());
+ }
+ }
+
+ /*
+ * Synchronize the Device Management View by setting the selected instance
+ *
+ * @param instanceName the Device Instance name
+ */
+ @SuppressWarnings("unused")
+ private void syncDeviceView(String instanceName)
+ {
+
+ try
+ {
+ InstanceRegistry registry = InstanceRegistry.getInstance();
+ List<IInstance> tmlInstances = registry.getInstancesByName(instanceName);
+ if (tmlInstances.size() > 0)
+ {
+ IInstance tmlInstance = tmlInstances.get(0);
+ InstanceMgtView.setSeletectedInstance(tmlInstance);
+ }
+ else
+ {
+ StudioLogger.warn("Could not synchronize with Device Management View: "
+ + instanceName + " not in TmL InstanceManager model");
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Could not synchronize with Device Management View: "
+ + e.getMessage());
+ }
+
+ }
+
+ /*
+ * Synchronize the DDMS Device View by setting the selected instance
+ *
+ * @param instanceName the Device Instance name
+ */
+ @SuppressWarnings("unused")
+ private void syncDDMSView(String instanceName)
+ {
+ try
+ {
+ IDevice device = DDMSFacade.getDeviceWithVmName(instanceName);
+ if (device != null)
+ {
+ DdmsPlugin.getDefault().selectionChanged(device, null);
+ }
+ else
+ {
+ StudioLogger
+ .warn("Could not synchronize with DDMS Devices View: Could not retrieve Device object from ADT model");
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.error("Could not synchronize with DDMS Devices View: " + e.getMessage());
+ }
+
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AbstractPropertiesComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AbstractPropertiesComposite.java
new file mode 100644
index 0000000..8909b19
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AbstractPropertiesComposite.java
@@ -0,0 +1,249 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.ui;
+
+import java.util.Collection;
+import java.util.EventListener;
+import java.util.EventObject;
+import java.util.LinkedHashSet;
+
+import org.eclipse.sequoyah.device.framework.events.IInstanceListener;
+import org.eclipse.sequoyah.device.framework.events.InstanceAdapter;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class is an abstract implementation of a Composite extension that specific composites
+ * for making Android Emulator Device Instance UI may use.
+ * <br>
+ * It provides common functionalities to those subclasses which assist content validation,
+ * layout, etc.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Provide common functionalities to classes implementing Android Emulator Device Instance UI
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * Composite: extends this class
+ * <br>
+ * MagxPropertyCompositeChangeListener: declares this interface for other classes to be able to
+ * listen to change events on the content
+ * <br>
+ * MagxPropertyCompositeChangeEvent: declares the class and uses this kind of event for
+ * notifying content change to listeners
+ * <br>
+ * USAGE:
+ * <br>
+ * This class should be extended by UI classes implementing Android Emulator Device Instance
+ * UI (property pages, wizards, etc).
+ */
+public abstract class AbstractPropertiesComposite extends Composite
+{
+ /**
+ *
+ * This class represents events for notifying content change on AbstractPropertiesComposite
+ * extending classes to registered listeners.
+ *
+ */
+ @SuppressWarnings("serial")
+ public class PropertyCompositeChangeEvent extends EventObject
+ {
+ /**
+ * Creates a new MagxPropertyCompositeChangeEvent object with the composite
+ * that changed as its data.
+ *
+ * @param composite the composite that changed
+ */
+ PropertyCompositeChangeEvent(AbstractPropertiesComposite composite)
+ {
+ super(composite);
+ }
+ }
+
+ /**
+ *
+ * This interface must be implemented by classes that wish to listen to
+ * changes on AbstractPropertiesComposite extending classes.
+ *
+ */
+ public interface PropertyCompositeChangeListener extends EventListener
+ {
+ /**
+ * Notifies the event of change on AbstractPropertiesComposite extending classes.
+ *
+ * @param e the change event
+ */
+ public void compositeChanged(PropertyCompositeChangeEvent e);
+ }
+
+ private class PropertyInstanceListener extends InstanceAdapter
+ {
+ private Collection<Control> affectedControls;
+
+ public PropertyInstanceListener(Collection<Control> affectedControls)
+ {
+ this.affectedControls = affectedControls;
+ }
+
+ @Override
+ public void instanceUpdated(final InstanceEvent e)
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ IInstance instance = e.getInstance();
+ boolean editable;
+ if (instance.getStatus().equals(EmulatorPlugin.STATUS_ONLINE_ID))
+ {
+ editable = false;
+ }
+ else
+ {
+ editable = true;
+ }
+ updateWidgetEnableStatus(editable, affectedControls);
+ }
+ });
+ }
+ }
+
+ // The default value to use for margins, both vertical and horizontal
+ private static final int DEFAULT_MARGIN_SIZE = 20;
+
+ // collection of registered listeners to this composite
+ private static final Collection<PropertyCompositeChangeListener> listeners =
+ new LinkedHashSet<PropertyCompositeChangeListener>();
+
+ // The listener that guarantees that if a instance is started it cannot be
+ // edited by the tool
+ private IInstanceListener instanceListener;
+
+ public AbstractPropertiesComposite(Composite parent)
+ {
+ super(parent, SWT.NONE);
+ }
+
+ /**
+ * Adds the given listener to the list of registered listeners of this composite's changes.
+ *
+ * @param listener the listener to be registered
+ */
+ public static void addCompositeChangeListener(PropertyCompositeChangeListener listener)
+ {
+ listeners.add(listener);
+ }
+
+ /**
+ * Removes the given listener from the list of registered listeners of this composite's changes.
+ *
+ * @param listener the listener to be unregistered
+ */
+ public static void removeCompositeChangeListener(PropertyCompositeChangeListener listener)
+ {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Notifies an event of change on the composite to all registered listeners.
+ *
+ * This method must be called by extending classes whenever a change is made to them
+ * that registered listeners should be aware of (examples: typed text, button press, etc)
+ */
+ protected void notifyCompositeChangeListeners()
+ {
+ PropertyCompositeChangeEvent event = new PropertyCompositeChangeEvent(this);
+
+ for (PropertyCompositeChangeListener listener : listeners)
+ {
+ listener.compositeChanged(event);
+ }
+ }
+
+ protected void addInstanceListener(Collection<Control> affectedControls)
+ {
+ instanceListener = new PropertyInstanceListener(affectedControls);
+ InstanceEventManager.getInstance().addInstanceListener(instanceListener);
+ }
+
+ /**
+ * Given a collection of controls, turn all their enabled status to
+ * the one provided
+ *
+ * @param enabled True to enable all controls in the collection
+ * @param controlsToUpdate A collection of all controls to apply the state provided by enabled parameter
+ */
+ protected void updateWidgetEnableStatus(boolean enabled, Collection<Control> controlsToUpdate)
+ {
+ for (Control control : controlsToUpdate)
+ {
+ if (!control.isDisposed())
+ {
+ control.setEnabled(enabled);
+ }
+ }
+ }
+
+ /**
+ * Sets the main layout to this composite as a GridLayout with the
+ * given number of columns and with columns not the same width.
+ *
+ * @param numColumns the number of columns for the GridLayout
+ */
+ protected final void setMainLayout(int numColumns)
+ {
+ GridLayout mainLayout = new GridLayout(numColumns, false);
+ mainLayout.marginWidth = DEFAULT_MARGIN_SIZE;
+ mainLayout.marginHeight = DEFAULT_MARGIN_SIZE;
+ this.setLayout(mainLayout);
+ }
+
+ /**
+ * Declaration of the abstract getErrorMessage() method.
+ * Retrieves the error message associated with the current extending composite
+ * state.
+ * If no error is found with the current state, <code>null</code> <b>must</b>
+ * be returned by the extending composite.
+ *
+ * @return the error message, or <code>null</code> if there are no errors
+ */
+ public abstract String getErrorMessage();
+
+ /**
+ * @see Widget#dispose()
+ */
+ @Override
+ public void dispose()
+ {
+ if (instanceListener != null)
+ {
+ InstanceEventManager.getInstance().removeInstanceListener(instanceListener);
+ }
+ super.dispose();
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AndroidPropertiesPage.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AndroidPropertiesPage.java
new file mode 100644
index 0000000..83571e0
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AndroidPropertiesPage.java
@@ -0,0 +1,185 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.ui;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent.InstanceEventType;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IWorkbenchPropertyPage;
+import org.eclipse.ui.dialogs.PropertyPage;
+
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite.PropertyCompositeChangeEvent;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite.PropertyCompositeChangeListener;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class implements the Property Page for Android Emulator Device Instances.
+ * <br>
+ * It shows all Android Emulator Device Instance properties on the UI so that the user
+ * is able to edit it (the instance name is not a property and will not be editable).
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Allow viewing and editing of Android Emulator Device Instance properties
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * PropertyPage: extends this class
+ * <br>
+ * InfoComposite: uses this composite for exhibiting instance properties on the UI
+ * <br>
+ * USAGE:
+ * <br>
+ * This class should be defined by the plugin.xml file as a regular Eclipse Property Page.
+ * It should be enabled for AndroidEmulatorInstance objects.
+ */
+public class AndroidPropertiesPage extends PropertyPage implements IWorkbenchPropertyPage,
+ IDevicePropertiesConstants
+{
+ // the Android Emulator Device Instance to which this Property Page applies
+ private AndroidDeviceInstance emuInstance;
+
+ private InfoComposite infoComposite;
+
+ // whether this property page will need its default message to be reset
+ // this happens in case the initial state of the property page when it is
+ // opened is an erroneous state (any of the properties contain invalid value)
+ private boolean defaultMessageNeedsReset = false;
+
+ // the default message defined by Eclipse implementation for reset purposes
+ private String defaultMessage = getMessage();
+
+ // handle changes
+ private PropertyCompositeChangeListener compositeChangeListener =
+ new PropertyCompositeChangeListener()
+ {
+ public void compositeChanged(PropertyCompositeChangeEvent e)
+ {
+ String errorMessage = infoComposite.getErrorMessage();
+ setErrorMessage(errorMessage);
+ setValid((errorMessage == null) && (getMessage() == null));
+
+ if (defaultMessageNeedsReset)
+ {
+ defaultMessageNeedsReset = false;
+ setMessage(defaultMessage);
+ }
+ }
+ };
+
+ /**
+ * Creates the UI contents of this Property Page.
+ * It shows the Android Emulator Device Instance properties
+ * organized into tabs.
+ */
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ ((PreferenceDialog) this.getContainer()).getTreeViewer().expandAll();
+
+ noDefaultAndApplyButton();
+
+ GridLayout mainLayout = new GridLayout(1, false);
+ mainLayout.marginWidth = 0;
+ mainLayout.marginHeight = 0;
+ Composite composite = new Composite(parent, SWT.NULL);
+ composite.setLayout(mainLayout);
+
+ infoComposite =
+ new InfoComposite(composite, emuInstance.getProperties(), emuInstance.getName(),
+ !emuInstance.isStarted());
+ infoComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ AbstractPropertiesComposite.addCompositeChangeListener(compositeChangeListener);
+
+ // there may be some info message for the composite
+ String initialMessage = infoComposite.getInfoMessage();
+
+ // if no info message, check if there is some error message
+ if (initialMessage == null)
+ {
+ // if anything is not correct with instance property values,
+ // show the error message, but as an information to follow
+ // UI guidelines
+ initialMessage = infoComposite.getErrorMessage();
+
+ setValid((initialMessage == null));
+ }
+
+ if (initialMessage != null)
+ {
+ defaultMessageNeedsReset = true;
+ setMessage(initialMessage, INFORMATION);
+ }
+
+ return composite;
+ }
+
+ /**
+ * Sets the element that owns the properties
+ */
+ @Override
+ public void setElement(IAdaptable element)
+ {
+ // save the instance for direct use
+ if (element instanceof AndroidDeviceInstance)
+ {
+ emuInstance = (AndroidDeviceInstance) element;
+ }
+
+ super.setElement(element);
+ }
+
+ /**
+ * Performs the OK operation by setting the edited properties as the
+ * properties for the Android Emulator Device Instance to which this
+ * Property Page applies (the object for which it was created).
+ */
+ @Override
+ public boolean performOk()
+ {
+ if (emuInstance != null)
+ {
+ emuInstance.setProperties(infoComposite.getPropertiesWorkingCopy());
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED, emuInstance));
+ }
+
+ return super.performOk();
+ }
+
+ /**
+ * Remove listeners and dispose widgets
+ */
+ @Override
+ public void dispose()
+ {
+ AbstractPropertiesComposite.removeCompositeChangeListener(compositeChangeListener);
+ infoComposite.dispose();
+ super.dispose();
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AndroidPropertiesStartupOptionsPage.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AndroidPropertiesStartupOptionsPage.java
new file mode 100644
index 0000000..5888ab9
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/AndroidPropertiesStartupOptionsPage.java
@@ -0,0 +1,228 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.ui;
+
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent.InstanceEventType;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IWorkbenchPropertyPage;
+import org.eclipse.ui.dialogs.PropertyPage;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.core.skin.SkinFramework;
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+import com.motorola.studio.android.emulator.device.instance.options.StartupOptionsMgt;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite.PropertyCompositeChangeEvent;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite.PropertyCompositeChangeListener;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class implements the Startup Options Property Page for Android Emulator Device Instances.
+ * <br>
+ * It shows the Startup Options for the Android Emulator Device Instance on the UI so that the user
+ * is able to edit it.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Allow viewing and editing Startup Options of an Android Emulator Device Instance
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * PropertyPage: extends this class
+ * <br>
+ * StartupOptionsComposite: uses this composite for exhibiting startup options on the UI
+ * <br>
+ * USAGE:
+ * <br>
+ * This class should be defined by the plugin.xml file as a regular Eclipse Property Page.
+ * It should be enabled for AndroidEmulatorInstance objects.
+ *
+ */
+public class AndroidPropertiesStartupOptionsPage extends PropertyPage implements
+ IWorkbenchPropertyPage, IDevicePropertiesConstants
+{
+
+ // the Android Emulator Device Instance to which this Property Page applies
+ private AndroidDeviceInstance emuInstance;
+
+ private StartupOptionsComposite startupOptionsComposite;
+
+ // whether this property page will need its default message to be reset
+ // this happens in case the initial state of the property page when it is
+ // opened is an erroneous state (any of the properties contain invalid value)
+ private boolean defaultMessageNeedsReset = false;
+
+ // the default message defined by Eclipse implementation for reset purposes
+ private final String defaultMessage = getMessage();
+
+ // handle changes
+ private final PropertyCompositeChangeListener compositeChangeListener =
+ new PropertyCompositeChangeListener()
+ {
+ public void compositeChanged(PropertyCompositeChangeEvent e)
+ {
+ String errorMessage = startupOptionsComposite.getErrorMessage();
+ setErrorMessage(errorMessage);
+ setValid((errorMessage == null) && (getMessage() == null));
+
+ if (defaultMessageNeedsReset)
+ {
+ defaultMessageNeedsReset = false;
+ setMessage(defaultMessage);
+ }
+ }
+ };
+
+ /**
+ * Creates the UI contents of this Property Page.
+ * It shows the Android Emulator Device Instance properties
+ * organized into tabs.
+ */
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ // Create Startup Options area
+
+ SkinFramework sm = new SkinFramework();
+ IAndroidSkin skin = null;
+ boolean canCalculateScale = true;
+ try
+ {
+ skin = sm.getSkinById(emuInstance.getSkinId(), emuInstance.getSkinPath());
+ }
+ catch (SkinException e)
+ {
+ StudioLogger.error(this.getClass(),
+ "Error reading instance skin during startup options page creation", e);
+ canCalculateScale = false;
+ }
+
+ startupOptionsComposite =
+ new StartupOptionsComposite(parent, emuInstance.getCommandLineArguments(), skin,
+ canCalculateScale);
+ startupOptionsComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ AbstractPropertiesComposite.addCompositeChangeListener(compositeChangeListener);
+
+ // If anything is not correct with instance property values,
+ // show the error message, but as an information to follow
+ // UI guidelines
+ String errorMessage = startupOptionsComposite.getErrorMessage();
+ setValid((errorMessage == null));
+ if (errorMessage != null)
+ {
+ defaultMessageNeedsReset = true;
+ setMessage(errorMessage, INFORMATION);
+ }
+
+ return startupOptionsComposite;
+
+ }
+
+ /**
+ * Sets the element that owns the properties
+ */
+ @Override
+ public void setElement(IAdaptable element)
+ {
+ // save the instance for direct use
+ if (element instanceof AndroidDeviceInstance)
+ {
+ emuInstance = (AndroidDeviceInstance) element;
+ }
+
+ super.setElement(element);
+ }
+
+ /**
+ * Performs the OK operation by setting the edited startup options as the
+ * startup options for the Android Emulator Device Instance to which this
+ * Property Page applies (the object for which it was created).
+ *
+ * @see org.eclipse.jface.preference.PreferencePage#performOk()
+ */
+ @Override
+ public boolean performOk()
+ {
+ save();
+ return super.performOk();
+ }
+
+ /**
+ * Performs the Apply operation (which is the same as OK)
+ *
+ * @see org.eclipse.jface.preference.PreferencePage#performApply()
+ */
+ @Override
+ protected void performApply()
+ {
+ save();
+ super.performApply();
+ }
+
+ /**
+ * Save the edited startup options in the Android Emulator Device Instance
+ */
+ private void save()
+ {
+ if (emuInstance != null)
+ {
+ Properties emuProperties = emuInstance.getProperties();
+ emuProperties.setProperty(IDevicePropertiesConstants.commandline,
+ StartupOptionsMgt.getParamList());
+ emuInstance.setProperties(emuProperties);
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED, emuInstance));
+ }
+ }
+
+ /**
+ * Set the default initial properties
+ *
+ * @see org.eclipse.jface.preference.PreferencePage#performDefaults()
+ */
+ @Override
+ protected void performDefaults()
+ {
+ startupOptionsComposite.reloadValues(NativeUIUtils.getDefaultCommandLine());
+ super.performDefaults();
+ }
+
+ /**
+ * Remove listeners and dispose widgets
+ */
+ @Override
+ public void dispose()
+ {
+ AbstractPropertiesComposite.removeCompositeChangeListener(compositeChangeListener);
+ startupOptionsComposite.dispose();
+ startupOptionsComposite = null;
+ super.dispose();
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/DevicePropertiesPage.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/DevicePropertiesPage.java
new file mode 100644
index 0000000..34915b6
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/DevicePropertiesPage.java
@@ -0,0 +1,47 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.ui;
+
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.ui.IWorkbenchPropertyPage;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.devices.AbstractDevicePropertyPage;
+
+public class DevicePropertiesPage extends AbstractDevicePropertyPage implements
+ IWorkbenchPropertyPage
+{
+
+ private ISerialNumbered androidIntance;
+
+ @Override
+ public void setElement(IAdaptable element)
+ {
+
+ this.androidIntance = (ISerialNumbered) element;
+
+ super.setElement(element);
+ }
+
+ @Override
+ protected Properties getDeviceProperties()
+ {
+ return DDMSFacade.getDeviceProperties(androidIntance.getSerialNumber());
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/DpiScaleCalculatorDialog.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/DpiScaleCalculatorDialog.java
new file mode 100644
index 0000000..0e3a015
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/DpiScaleCalculatorDialog.java
@@ -0,0 +1,383 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.ui;
+
+import java.awt.Dimension;
+import java.awt.Toolkit;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.core.skin.ISkinKeyXmlTags;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+public class DpiScaleCalculatorDialog extends Dialog
+{
+ private Text screenSizeValue;
+
+ private Button monitorDpiValueButton;
+
+ private Text monitorDpiValue;
+
+ private Text monitorSizeText;
+
+ private Text resultDpivalueText;
+
+ private Integer resultDpiValue;
+
+ private Label resultScaleText;
+
+ private Double resultScaleValue;
+
+ private Text resultScaleValueText;
+
+ private Label errorLabel;
+
+ private final IAndroidSkin skin;
+
+ private final Collection<String> errors = new ArrayList<String>();
+
+ int size1 = -1;
+
+ int size2 = -1;
+
+ /**
+ * The Dialog constructor
+ *
+ * @param parentShell The shell
+ * @param skin The selected skin of the AVD being created/edited
+ */
+ protected DpiScaleCalculatorDialog(Shell parentShell, IAndroidSkin skin)
+ {
+ super(parentShell);
+ this.skin = skin;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
+ */
+ @Override
+ protected void configureShell(Shell newShell)
+ {
+ super.configureShell(newShell);
+ newShell.setText(EmulatorNLS.DPISCALECALCULATOR_Title);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createDialogArea(Composite parent)
+ {
+ //Main Composite
+ Composite mainComposite = new Composite(parent, SWT.FILL);
+ mainComposite.setLayout(new GridLayout(2, false));
+ GridData data;
+
+ //Error Area
+ errorLabel = new Label(mainComposite, SWT.READ_ONLY);
+ errorLabel.setForeground(new Color(null, 255, 0, 0));
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1);
+ errorLabel.setLayoutData(data);
+
+ //Screen Size
+ Label screenSizeLabel = new Label(mainComposite, SWT.READ_ONLY);
+ screenSizeLabel.setText(EmulatorNLS.DPISCALECALCULATOR_ScreenSize_Label);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ screenSizeLabel.setLayoutData(data);
+
+ screenSizeValue = new Text(mainComposite, SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.NULL, true, false);
+ screenSizeValue.setLayoutData(data);
+ screenSizeValue.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ validate();
+ }
+ });
+
+ //Monitor DPI Group
+ Group monitorDpiGroup = new Group(mainComposite, SWT.SHADOW_OUT);
+ monitorDpiGroup.setText(EmulatorNLS.DPISCALECALCULATOR_MonitorDpi_Label);
+ data = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
+ monitorDpiGroup.setLayoutData(data);
+ monitorDpiGroup.setLayout(new GridLayout(3, false));
+
+ //Insert Monitor DPI value Option
+ monitorDpiValueButton = new Button(monitorDpiGroup, SWT.RADIO);
+ monitorDpiValueButton.setText(EmulatorNLS.DPISCALECALCULATOR_MonitorDpivalue_Label);
+ monitorDpiValueButton.setSelection(true);
+ monitorDpiValueButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ monitorDpiValue.setEnabled(monitorDpiValueButton.getSelection());
+ validate();
+ }
+ });
+ data = new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1);
+ monitorDpiValueButton.setLayoutData(data);
+
+ monitorDpiValue = new Text(monitorDpiGroup, SWT.SINGLE | SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1);
+ data.widthHint = 100;
+ monitorDpiValue.setLayoutData(data);
+ monitorDpiValue.setEnabled(monitorDpiValueButton.getSelection());
+ monitorDpiValue.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ validate();
+ }
+ });
+
+ //Calculate Monitor DPI Option
+ final Button calculateMonitorDpiButton = new Button(monitorDpiGroup, SWT.RADIO);
+ calculateMonitorDpiButton.setText(EmulatorNLS.DPISCALECALCULATOR_MonitorDpiSize_Label);
+ calculateMonitorDpiButton.setSelection(false);
+ calculateMonitorDpiButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ monitorSizeText.setEnabled(calculateMonitorDpiButton.getSelection());
+ validate();
+ }
+ });
+
+ monitorSizeText = new Text(monitorDpiGroup, SWT.SINGLE | SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
+ monitorSizeText.setLayoutData(data);
+ monitorSizeText.setEnabled(calculateMonitorDpiButton.getSelection());
+ monitorSizeText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ validate();
+ }
+ });
+
+ //Result Group
+ Group resultGroup = new Group(mainComposite, SWT.SHADOW_OUT);
+ resultGroup.setText(EmulatorNLS.DPISCALECALCULATOR_ResultGroup_Title);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1);
+ resultGroup.setLayoutData(data);
+ resultGroup.setLayout(new GridLayout(4, false));
+
+ //Moitor DPI
+ Label resultDpi = new Label(resultGroup, SWT.READ_ONLY);
+ resultDpi.setText(EmulatorNLS.DPISCALECALCULATOR_ResultMonitorDpi_Label);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ resultDpi.setLayoutData(data);
+
+ resultDpivalueText = new Text(resultGroup, SWT.WRAP | SWT.READ_ONLY);
+ data = new GridData(SWT.FILL, SWT.NULL, true, false);
+ resultDpivalueText.setLayoutData(data);
+
+ //Scale
+ resultScaleText = new Label(resultGroup, SWT.READ_ONLY);
+ resultScaleText.setText(EmulatorNLS.DPISCALECALCULATOR_ResultScale_Label);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ resultScaleText.setLayoutData(data);
+
+ resultScaleValueText = new Text(resultGroup, SWT.WRAP | SWT.READ_ONLY);
+ data = new GridData(SWT.FILL, SWT.NULL, true, false);
+ resultScaleValueText.setLayoutData(data);
+
+ mainComposite.layout();
+ mainComposite.pack();
+
+ return null;
+ }
+
+ /**
+ * Updates the Monitor DPI and Scale results
+ *
+ */
+ private void updateResult()
+ {
+
+ if (monitorDpiValueButton.getSelection())
+ {
+ resultDpiValue = Integer.parseInt(monitorDpiValue.getText());
+ }
+ else
+ {
+ resultDpiValue = calculateMonitorDpi();
+ }
+ resultDpivalueText.setText(resultDpiValue.toString());
+
+ resultScaleValue = calculateScale();
+ resultScaleValueText.setText(resultScaleValue.toString());
+ }
+
+ /**
+ * Calculates the Monitor DPI using the user monitor size and the resolution
+ *
+ * @return int The calculated Monitor DPI
+ */
+ private int calculateMonitorDpi()
+ {
+ float monitorSize = Float.parseFloat(monitorSizeText.getText());
+ Dimension b = Toolkit.getDefaultToolkit().getScreenSize();
+ float width = b.width;
+ float height = b.height;
+ float ratio = width / height;
+
+ double dpi =
+ Math.round((width / (ratio * (Math.sqrt((Math.pow(monitorSize, 2))
+ / (1f + Math.pow(ratio, 2)))))));
+
+ return (int) dpi;
+ }
+
+ /**
+ * Calculates the scale to be used using the monitor dpi, the device screen size and the skin main display dimensions
+ *
+ * @return
+ */
+ private double calculateScale()
+ {
+ double dpi = resultDpiValue;
+
+ if ((skin != null) && (size1 == -1) && (size2 == -1))
+ {
+ try
+ {
+ Collection<String> layouts = skin.getAvailableLayouts();
+ String defLayout = layouts.toArray()[0].toString();
+
+ size1 =
+ skin.getSkinBean(defLayout).getSkinPropertyValue(
+ ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH);
+
+ size2 =
+ skin.getSkinBean(defLayout).getSkinPropertyValue(
+ ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT);
+ }
+ catch (SkinException e)
+ {
+ StudioLogger.error(DpiScaleCalculatorDialog.class, "Error while calculating scale", //$NON-NLS-1$
+ e);
+ }
+
+ }
+ if ((size1 > 0) && (size2 > 0))
+ {
+ double diagonalPx = Math.sqrt(Math.pow(size1, 2) + Math.pow(size2, 2));
+ double screenSize = Double.parseDouble(screenSizeValue.getText());
+
+ double scale = (screenSize * dpi) / diagonalPx;
+ return (Math.round((scale * 100.0))) / 100.0;
+ }
+ else
+ {
+ getButton(IDialogConstants.OK_ID).setEnabled(false);
+ }
+ return 1;
+ }
+
+ /**
+ * Validates all the calculator fields
+ */
+ public void validate()
+ {
+ final String REGEX_1 = EmulatorNLS.DPISCALECALCULATOR_Regex_TwoDigits;
+ final String REGEX_2 = "\\d+"; //$NON-NLS-1$
+
+ final String ERROR_SCREEN_SIZE = EmulatorNLS.DPISCALECALCULATOR_Error_ScreenSize;
+ final String ERROR_DPI_VALUE = EmulatorNLS.DPISCALECALCULATOR_Error_MonitorDpi;
+ final String ERROR_MONITOR_SIZE = EmulatorNLS.DPISCALECALCULATOR_Error_MonitorSize;
+
+ errors.clear();
+ errorLabel.setText(""); //$NON-NLS-1$
+
+ if (!screenSizeValue.getText().matches(REGEX_1))
+ {
+ errors.add(ERROR_SCREEN_SIZE);
+ }
+
+ if (monitorDpiValueButton.getSelection() && !monitorDpiValue.getText().matches(REGEX_2))
+ {
+ errors.add(ERROR_DPI_VALUE);
+ }
+
+ if (!monitorDpiValueButton.getSelection() && !monitorSizeText.getText().matches(REGEX_1))
+ {
+ errors.add(ERROR_MONITOR_SIZE);
+ }
+
+ if (errors.size() > 0)
+ {
+ getButton(OK).setEnabled(false);
+ errorLabel.setText((String) errors.toArray()[0]);
+ errorLabel.pack();
+
+ resultDpivalueText.setText(""); //$NON-NLS-1$
+ resultScaleValueText.setText(""); //$NON-NLS-1$
+ }
+ else
+ {
+ getButton(OK).setEnabled(true);
+ updateResult();
+ }
+
+ }
+
+ /**
+ * Gets the calculated device dpi
+ *
+ * @return the device dpi to be used
+ */
+ public String getResultDpivalue()
+ {
+ return resultDpiValue.toString();
+ }
+
+ /**
+ * Gets the calculated scale
+ *
+ * @return the scale to be used
+ */
+ public String getResultScaleValue()
+ {
+ return resultScaleValue.toString();
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/InfoComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/InfoComposite.java
new file mode 100644
index 0000000..278de88
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/InfoComposite.java
@@ -0,0 +1,210 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.ui;
+
+import java.util.Properties;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class is a composite which shows all Android Emulator Device Instance information.
+ * All displayed information can be edited and kept on a Properties object, which can be
+ * later used to be set to an actual Android Emulator Device Instance object as its new values.
+ * <br>
+ * It extends the AbstractPropertiesComposite so as to use its common functionalities.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Show all available UI information of a Android Emulator Device Instance
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * AbstractPropertiesComposite: extends this class
+ * <br>
+ * IDevicePropertiesConstants: implements this interface in order to have direct use of its
+ * constants for populating the device instance's Properties object
+ * <br>
+ * USAGE:
+ * <br>
+ * This composite can be used for any UI that shows all Android Emulator Device Instance
+ * UI information. It can either allow of stop the editing of the name of the instance,
+ * while other information is always editable.
+ * It should be instantiated as a regular composite.
+ */
+public class InfoComposite extends AbstractPropertiesComposite implements
+ IDevicePropertiesConstants
+{
+
+ private boolean showEmulatorDefNotFoundMsg = false;
+
+ private Properties emuPropertiesWorkingCopy;
+
+ private String emuName;
+
+ private PropertiesMainComposite mainComposite;
+
+ // the listener to changes on all AbstractPropertiesComposite objects used on this composite
+ private PropertyCompositeChangeListener listener = new PropertyCompositeChangeListener()
+ {
+ public void compositeChanged(PropertyCompositeChangeEvent e)
+ {
+ if (e.getSource() instanceof PropertiesMainComposite)
+ {
+ emuPropertiesWorkingCopy.setProperty(timeout, mainComposite.getTimeout());
+ emuPropertiesWorkingCopy.setProperty(skinId, mainComposite.getSkinId());
+ emuPropertiesWorkingCopy.setProperty(useVnc, mainComposite.getUseVnc());
+ emuPropertiesWorkingCopy.setProperty(useProxy, mainComposite.getUseProxy());
+ emuPropertiesWorkingCopy.setProperty(startFromSnapshot,
+ mainComposite.getstartFromSnapshot());
+ emuPropertiesWorkingCopy.setProperty(saveSnapshot, mainComposite.getSaveSnapshot());
+ emuPropertiesWorkingCopy.setProperty(abiType, mainComposite.getAbiType());
+
+ notifyCompositeChangeListeners();
+ }
+ }
+ };
+
+ /**
+ * Creates a InfoComposite object.
+ *
+ * @param parent the parent composite
+ * @param emuProperties the instance properties to be shown on the UI
+ * @param emuName the name of the instance
+ * @param isNameEditable whether the instance name should be made editable or not
+ * @param areOtherFieldsEditable True if the user will be able to edit other data in the composite; false otherwise
+ */
+ public InfoComposite(Composite parent, Properties emuProperties, String emuName,
+ boolean areOtherFieldsEditable)
+ {
+ super(parent);
+
+ this.emuPropertiesWorkingCopy = (Properties) emuProperties.clone();
+ this.emuName = emuName;
+
+ createUI(areOtherFieldsEditable);
+
+ parent.addDisposeListener(new DisposeListener()
+ {
+ public void widgetDisposed(DisposeEvent e)
+ {
+ AbstractPropertiesComposite.removeCompositeChangeListener(listener);
+ InfoComposite.this.dispose();
+ }
+ });
+ }
+
+ /**
+ * Creates all the UI for this composite. The listener declared as attribute to this class
+ * is added to all composites extending AbstractPropertiesComposite.
+ * Information is organized on 3 tabs:<br>
+ * - "Main" tab: uses the PropertiesMainComposite;<br>
+ * - "Advanced" tab: uses the PropertiesAdvancedComposite;<br>
+ *
+ * @param editable True if the user will be able to edit data in the composite; false otherwise
+ */
+ private void createUI(boolean editable)
+ {
+ GridLayout mainLayout = new GridLayout(1, false);
+ mainLayout.marginWidth = 0;
+ mainLayout.marginHeight = 0;
+ this.setLayout(mainLayout);
+
+ mainComposite =
+ new PropertiesMainComposite(this, emuName, getProperty(emulatorDefId),
+ getProperty(timeout), Boolean.parseBoolean(getProperty(useVnc)),
+ Boolean.parseBoolean(getProperty(useProxy)),
+ Boolean.parseBoolean(getProperty(useSnapshots)),
+ Boolean.parseBoolean(getProperty(saveSnapshot)),
+ Boolean.parseBoolean(getProperty(startFromSnapshot)),
+ SdkUtils.getTargetByName(getProperty(vmTarget)), getProperty(vmSkin),
+ getProperty(vmPath), getProperty(abiType), true, false, editable);
+ mainComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ AbstractPropertiesComposite.addCompositeChangeListener(listener);
+ }
+
+ /**
+ * Retrieves the given property from the instance.
+ *
+ * @param propertyName the name of the property (key)
+ *
+ * @return the property value
+ */
+ private String getProperty(String propertyName)
+ {
+ String emuProperty = emuPropertiesWorkingCopy.getProperty(propertyName);
+ if (emuProperty == null)
+ {
+ emuProperty = "";
+ }
+ return emuProperty;
+ }
+
+ /**
+ * Retrieves the (potentially) edited properties.
+ *
+ * @return the edited properties
+ */
+ public Properties getPropertiesWorkingCopy()
+ {
+ return emuPropertiesWorkingCopy;
+ }
+
+ /**
+ * Retrieves the error message associated to this composites current state.
+ * "Main" tab's error message is returned if any; if none, "Skin" tab's error
+ * message is returned if any; if none, "VM" tab's error message is returned if
+ * any; if none, <code>null</code> is returned to state there is no error with
+ * the current state.
+ *
+ * @return the error message, or <code>null</code> if there are no errors
+ */
+ @Override
+ public String getErrorMessage()
+ {
+ String errorMessage = mainComposite.getErrorMessage();
+
+ return errorMessage;
+ }
+
+ /**
+ * Retrieves the information message associated to this composites current state.
+ *
+ * @return the information message, or <code>null</code> if there are no information messages
+ */
+ public String getInfoMessage()
+ {
+ String infoMessage = null;
+
+ if (showEmulatorDefNotFoundMsg)
+ {
+ infoMessage = EmulatorNLS.INFO_InfoComposite_EmulatorDefinitionNotFound;
+ }
+
+ return infoMessage;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/PropertiesMainComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/PropertiesMainComposite.java
new file mode 100644
index 0000000..9c3b391
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/PropertiesMainComposite.java
@@ -0,0 +1,1287 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.ui;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.preference.IPreferenceNode;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.preference.PreferenceManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.internal.dialogs.WorkbenchPreferenceDialog;
+
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.ISystemImage;
+import com.android.sdklib.SdkConstants;
+import com.android.sdklib.internal.avd.AvdInfo;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.device.IAndroidDeviceConstants;
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.device.definition.AndroidEmuDefMgr;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class implements the UI for showing all Android Emulator Device Instance main information,
+ * such as its name, description, etc.
+ * <br>
+ * It extends the AbstractPropertiesComposite so as to use its common functionalities.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Show Android Emulator Device Instance main information on the UI
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * AbstractPropertiesComposite: extends this class
+ * <br>
+ * USAGE:
+ * <br>
+ * This class should be added as a regular composite whenever main information on Android Emulator
+ * Device Instance is necessary to be shown and edited on the UI. It can either allow of stop the
+ * editing of the name of the instance, while other information is always editable.
+ */
+//@SuppressWarnings("restriction")
+@SuppressWarnings("restriction")
+public class PropertiesMainComposite extends AbstractPropertiesComposite
+{
+ private static final String ORG_ECLIPSE_UI_NET_NET_PREFERENCES =
+ "org.eclipse.ui.net.NetPreferences"; //$NON-NLS-1$
+
+ private final AndroidEmuDefMgr emuDefMgr = AndroidEmuDefMgr.getInstance();
+
+ private String name = ""; //$NON-NLS-1$
+
+ private String skinId = ""; //$NON-NLS-1$
+
+ private String timeout = ""; //$NON-NLS-1$
+
+ private boolean useVnc;
+
+ private boolean useProxy;
+
+ private boolean useSnapshots;
+
+ // VM Settings
+ private IAndroidTarget vmTarget = null;
+
+ private String vmSkin = ""; //$NON-NLS-1$
+
+ private String sdCardType = ""; //$NON-NLS-1$
+
+ private String sdCardValue = ""; //$NON-NLS-1$
+
+ private String vmPath = ""; //$NON-NLS-1$
+
+ private boolean usingDefaultVmPath;
+
+ private String abiType = "";
+
+ /*
+ * SD Card
+ */
+ private static final String SDCARD_TYPE_NONE = "NONE"; //$NON-NLS-1$
+
+ private static final String SDCARD_TYPE_PATH = "PATH"; //$NON-NLS-1$
+
+ private static final String SDCARD_TYPE_SIZE = "SIZE"; //$NON-NLS-1$
+
+ private static final String SDCARD_PATH_EXTENSION = ".img"; //$NON-NLS-1$
+
+ private static final String[] SDCARD_SIZE_UNITS = new String[]
+ {
+ "KB", "MB" //$NON-NLS-1$ //$NON-NLS-2$
+ };
+
+ private Button saveSnapshotButton;
+
+ private Button startFromSnapshotButton;
+
+ private boolean saveSnapshots;
+
+ private boolean startFromSnapshots;
+
+ private Combo abiTypeCombo;
+
+ /**
+ * Creates a PropertiesMainComposite object.
+ *
+ * @param parent the parent composite
+ * @param name the instance name
+ * @param description the instance description
+ * @param emulatorDefId the instance emulator definition id
+ * @param isNameEditable whether the name should be editable or not
+ * @param areOtherFieldsEditable True if the user will be able to edit other data in the composite; false otherwise
+ */
+ public PropertiesMainComposite(Composite parent, String name, String emulatorDefId,
+ String timeout, boolean useVnc, boolean useProxy, boolean useSnapshot,
+ boolean saveSnapshot, boolean startFromSnapshot, IAndroidTarget vmTarget,
+ String vmSkin, String vmPath, String abiType, boolean showName,
+ boolean areVmSettingsEditable, boolean areOtherFieldsEditable)
+
+ {
+ super(parent);
+
+ this.name = name;
+ this.timeout = timeout;
+ this.useVnc = useVnc;
+ this.useProxy = useProxy;
+ this.abiType = abiType;
+ skinId = emuDefMgr.getSkinId(emulatorDefId);
+
+ this.vmTarget = vmTarget;
+ this.vmSkin = vmSkin;
+ this.vmPath = vmPath;
+ this.useSnapshots = useSnapshot;
+ this.saveSnapshots = saveSnapshot;
+ this.startFromSnapshots = startFromSnapshot;
+
+ createUI(showName, areVmSettingsEditable, areOtherFieldsEditable);
+
+ //Set context Help (not available yet)
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(parent, IAndroidDeviceConstants.MAIN_PAGE_HELP);
+ }
+
+ /**
+ * Creates a PropertiesMainComposite object with instance name editing not allowed.
+ *
+ * @param parent the parent composite
+ * @param name the instance name
+ * @param description the instance description
+ * @param emulatorDefId the instance emulator definition name
+ */
+ public PropertiesMainComposite(Composite parent, String emulatorDefId, String timeout,
+ boolean useVnc, boolean useProxy, boolean useSnapshot, boolean saveSnapshot,
+ boolean startFromSnapshot, IAndroidTarget vmTarget, String vmSkin, String vmPath,
+ String abiType)
+ {
+ this(parent, "", emulatorDefId, timeout, useVnc, useProxy, useSnapshot, saveSnapshot,
+ startFromSnapshot, vmTarget, vmSkin, vmPath, abiType, false, true, true);
+
+ //Set context Help (not available yet)
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(parent, IAndroidDeviceConstants.MAIN_PAGE_HELP);
+ }
+
+ /**
+ * @param showName
+ * @param areVmSettingsEditable
+ * @param areOtherFieldsEditable
+ */
+ private void createUI(boolean showName, boolean areVmSettingsEditable,
+ boolean areOtherFieldsEditable)
+ {
+ GridData data;
+ Composite mainComposite = this;
+ Collection<Control> otherFields = new HashSet<Control>();
+
+ setMainLayout(2);
+
+ if (showName)
+ {
+ Label nameLabel = new Label(mainComposite, SWT.READ_ONLY);
+ nameLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_NameLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ nameLabel.setLayoutData(data);
+
+ Text nameTextLabel = new Text(mainComposite, SWT.WRAP | SWT.READ_ONLY);
+ nameTextLabel.setText(name);
+ data = new GridData(SWT.FILL, SWT.NULL, true, false);
+ nameTextLabel.setLayoutData(data);
+ }
+
+ /*
+ * (Not) Editable area
+ */
+ if (areVmSettingsEditable)
+ {
+ createEditableVmUI();
+ }
+ else
+ {
+ createNotEditableVmUI();
+ }
+
+ /*
+ * Emulator proxy settings
+ */
+ Group proxyGroup = new Group(mainComposite, SWT.SHADOW_OUT);
+ proxyGroup.setText(EmulatorNLS.PropertiesMainComposite_ProxySettings_GroupTitle);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1);
+ proxyGroup.setLayoutData(data);
+ proxyGroup.setLayout(new GridLayout(3, false));
+
+ final Button proxyChkbox = new Button(proxyGroup, SWT.CHECK);
+ proxyChkbox.setText(EmulatorNLS.PropertiesMainComposite_ProxySettings_CheckboxLabel);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ proxyChkbox.setSelection(useProxy);
+ proxyChkbox.setLayoutData(data);
+ proxyChkbox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ useProxy = proxyChkbox.getSelection();
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ Link networkSettings = new Link(proxyGroup, SWT.NONE);
+ networkSettings.setText(EmulatorNLS.PropertiesMainComposite_ProxySettings_LinkToPreference);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1);
+ networkSettings.setLayoutData(data);
+
+ networkSettings.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ openNetworkPreferences();
+ }
+ });
+
+ //Snapshot group
+
+ Group snapshotGroup = new Group(mainComposite, SWT.SHADOW_OUT);
+ snapshotGroup.setText(EmulatorNLS.PropertiesMainComposite_SnapshotSettings);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1);
+ snapshotGroup.setLayoutData(data);
+ snapshotGroup.setLayout(new GridLayout(3, false));
+
+ final Button enableSnapshotButton = new Button(snapshotGroup, SWT.CHECK);
+ enableSnapshotButton.setText(EmulatorNLS.PropertiesMainComposite_UseSnapshot);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ enableSnapshotButton.setLayoutData(data);
+ enableSnapshotButton.setEnabled(areVmSettingsEditable);
+ enableSnapshotButton.setSelection(useSnapshots);
+ enableSnapshotButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ useSnapshots = enableSnapshotButton.getSelection();
+ notifyCompositeChangeListeners();
+ startFromSnapshotButton.setEnabled(useSnapshots);
+ saveSnapshotButton.setEnabled(useSnapshots);
+ }
+ });
+
+ startFromSnapshotButton = new Button(snapshotGroup, SWT.CHECK);
+ startFromSnapshotButton.setText(EmulatorNLS.PropertiesMainComposite_startFromSnapshot);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ startFromSnapshotButton.setLayoutData(data);
+ startFromSnapshotButton.setEnabled(useSnapshots);
+ startFromSnapshotButton.setSelection(startFromSnapshots);
+
+ startFromSnapshotButton.addSelectionListener(new SelectionAdapter()
+ {
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ startFromSnapshots = startFromSnapshotButton.getSelection();
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ saveSnapshotButton = new Button(snapshotGroup, SWT.CHECK);
+ saveSnapshotButton.setText(EmulatorNLS.PropertiesMainComposite_SaveSnapshot);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ saveSnapshotButton.setLayoutData(data);
+ saveSnapshotButton.setEnabled(useSnapshots);
+ saveSnapshotButton.setSelection(saveSnapshots);
+
+ saveSnapshotButton.addSelectionListener(new SelectionAdapter()
+ {
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ saveSnapshots = saveSnapshotButton.getSelection();
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ otherFields.add(proxyGroup);
+ otherFields.add(proxyChkbox);
+ otherFields.add(networkSettings);
+ otherFields.add(snapshotGroup);
+
+ /*
+ * Emulator Window mode area
+ */
+
+ //Only for windows and linux platforms
+ if (!Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ Group windowGroup = new Group(mainComposite, SWT.SHADOW_OUT);
+ windowGroup
+ .setText(EmulatorNLS.UI_PropertiesMainComposite_EmulatorWindowMode_GroupTitle);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1);
+ windowGroup.setLayoutData(data);
+ windowGroup.setLayout(new GridLayout(3, false));
+
+ final Button nativeRadio = new Button(windowGroup, SWT.RADIO);
+ nativeRadio
+ .setText(EmulatorNLS.UI_PropertiesMainComposite_EmulatorWindowMode_NativeLabel);
+ nativeRadio.setSelection(!useVnc);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ nativeRadio.setLayoutData(data);
+ nativeRadio.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ useVnc = !nativeRadio.getSelection();
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ final Button vncRadio = new Button(windowGroup, SWT.RADIO);
+ vncRadio.setText(EmulatorNLS.UI_PropertiesMainComposite_EmulatorWindowMode_VncLabel);
+ vncRadio.setSelection(useVnc);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ vncRadio.setLayoutData(data);
+
+ otherFields.add(windowGroup);
+ otherFields.add(nativeRadio);
+ otherFields.add(vncRadio);
+ }
+
+ /*
+ * Timeout
+ */
+ Label timeoutLabel = new Label(mainComposite, SWT.READ_ONLY);
+ timeoutLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_TimeoutLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ timeoutLabel.setLayoutData(data);
+
+ final Text timeoutText = new Text(mainComposite, SWT.SINGLE | SWT.BORDER);
+ otherFields.add(timeoutText);
+ timeoutText.setText(timeout);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ timeoutText.setLayoutData(data);
+
+ timeoutText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ timeout = timeoutText.getText().trim();
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ addInstanceListener(otherFields);
+ updateWidgetEnableStatus(areOtherFieldsEditable, otherFields);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void openNetworkPreferences()
+ {
+ // Makes the network preferences dialog manager
+ PreferenceManager manager = PlatformUI.getWorkbench().getPreferenceManager();
+ IPreferenceNode networkNode = null;
+ for (IPreferenceNode node : (List<IPreferenceNode>) manager
+ .getElements(PreferenceManager.PRE_ORDER))
+ {
+ if (node.getId().equals(ORG_ECLIPSE_UI_NET_NET_PREFERENCES))
+ {
+ networkNode = node;
+ break;
+ }
+ }
+ PreferenceManager prefMan = new PreferenceManager();
+ if (networkNode != null)
+ {
+ prefMan.addToRoot(networkNode);
+ }
+ PreferenceDialog networkPreferencesDialog =
+ new WorkbenchPreferenceDialog(getShell(), prefMan);
+ networkPreferencesDialog.create();
+ networkPreferencesDialog.open();
+ }
+
+ /**
+ * Creates the not editable UI for the VM settings.
+ */
+ private void createNotEditableVmUI()
+ {
+ GridData data;
+ Composite mainComposite = this;
+
+ Label tagetLabel = new Label(mainComposite, SWT.READ_ONLY);
+ tagetLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_TargetLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ tagetLabel.setLayoutData(data);
+
+ final Text targetText = new Text(mainComposite, SWT.WRAP | SWT.READ_ONLY);
+ targetText.setText(vmTarget.getName());
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ targetText.setLayoutData(data);
+
+ Label abiTypeLabel = new Label(mainComposite, SWT.READ_ONLY);
+ abiTypeLabel.setText(EmulatorNLS.PropertiesMainComposite_ABITypeLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ abiTypeLabel.setLayoutData(data);
+
+ final Text abiTypeText = new Text(mainComposite, SWT.WRAP | SWT.READ_ONLY);
+ abiTypeText.setText(AvdInfo.getPrettyAbiType(abiType));
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ abiTypeText.setLayoutData(data);
+
+ Label skinLabel = new Label(mainComposite, SWT.READ_ONLY);
+ skinLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_SkinLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ skinLabel.setLayoutData(data);
+
+ final Text vmSkinText = new Text(mainComposite, SWT.WRAP | SWT.READ_ONLY);
+ vmSkinText.setText(vmSkin);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ vmSkinText.setLayoutData(data);
+
+ Label pathLabel = new Label(mainComposite, SWT.READ_ONLY);
+ pathLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_PathLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ pathLabel.setLayoutData(data);
+
+ final Text pathText = new Text(mainComposite, SWT.WRAP | SWT.READ_ONLY);
+ pathText.setText(vmPath);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ data.widthHint = 100;
+ pathText.setLayoutData(data);
+
+ /*
+ * SD Card info
+ */
+ AvdInfo vmInfo = SdkUtils.getValidVm(name);
+ Properties configFile = new Properties();
+ BufferedReader input = null;
+ try
+ {
+
+ // it's necessary to read the file instead of load the Properties file because
+ // there are "\" in the path, which are removed by the Properties class load method, since
+ // they mean line break in a properties file
+ input = new BufferedReader(new FileReader(vmInfo.getConfigFile()));
+
+ String line = null;
+ String[] property = null;
+ while ((line = input.readLine()) != null)
+ {
+ property = line.split("="); //$NON-NLS-1$
+ if ((property[0] != null) && (property[1] != null))
+ {
+ if ((!property[0].equals("")) && (!property[1].equals(""))) //$NON-NLS-1$ //$NON-NLS-2$
+ {
+ configFile.put(property[0], property[1]);
+ }
+ }
+ }
+
+ if (configFile.getProperty(IDevicePropertiesConstants.configSDCardPath) != null)
+ {
+
+ Label sdCardPathLabel = new Label(mainComposite, SWT.READ_ONLY);
+ sdCardPathLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_SDCardPathLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ sdCardPathLabel.setLayoutData(data);
+
+ final Text sdCardPathText = new Text(mainComposite, SWT.WRAP | SWT.READ_ONLY);
+ sdCardPathText.setText(configFile
+ .getProperty(IDevicePropertiesConstants.configSDCardPath));
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ data.widthHint = 100;
+ sdCardPathText.setLayoutData(data);
+
+ }
+
+ if (configFile.getProperty(IDevicePropertiesConstants.configSDCardSize) != null)
+ {
+ Label sdCardPathLabel = new Label(mainComposite, SWT.READ_ONLY);
+ sdCardPathLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_SDCardSizeLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ sdCardPathLabel.setLayoutData(data);
+
+ final Text sdCardPathText = new Text(mainComposite, SWT.WRAP | SWT.READ_ONLY);
+ sdCardPathText.setText(configFile
+ .getProperty(IDevicePropertiesConstants.configSDCardSize));
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ sdCardPathText.setLayoutData(data);
+ }
+
+ }
+ catch (FileNotFoundException e)
+ {
+ StudioLogger.error("Could not find config file for AVD: " + name); //$NON-NLS-1$
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not read config file for AVD: " + name); //$NON-NLS-1$
+ }
+ finally
+ {
+ try
+ {
+ if (input != null)
+ {
+ input.close();
+ }
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close input stream: ", e.getMessage()); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /**
+ * Creates the editable UI for the VM settings.
+ */
+ private void createEditableVmUI()
+ {
+ GridData data;
+ Composite mainComposite = this;
+
+ Label tagetLabel = new Label(mainComposite, SWT.READ_ONLY);
+ tagetLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_TargetLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ tagetLabel.setLayoutData(data);
+
+ final Combo targetCombo = new Combo(mainComposite, SWT.READ_ONLY);
+ populateTargetCombo(targetCombo);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1);
+ targetCombo.setLayoutData(data);
+
+ Label skinLabel = new Label(mainComposite, SWT.READ_ONLY);
+ skinLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_SkinLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ skinLabel.setLayoutData(data);
+
+ final Combo vmSkinCombo = new Combo(mainComposite, SWT.READ_ONLY);
+ populateSkinCombo(vmSkinCombo);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1);
+ vmSkinCombo.setLayoutData(data);
+
+ vmSkinCombo.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if ((vmSkinCombo != null) && !"".equals(vmSkinCombo.getText())) //$NON-NLS-1$
+ {
+ vmSkin = (String) vmSkinCombo.getData(vmSkinCombo.getText());
+
+ }
+ else
+ {
+ vmSkin = ""; //$NON-NLS-1$
+ }
+ notifyCompositeChangeListeners();
+
+ }
+ });
+
+ Label abiTypeLabel = new Label(mainComposite, SWT.READ_ONLY);
+ abiTypeLabel.setText(EmulatorNLS.PropertiesMainComposite_ABITypeLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ abiTypeLabel.setLayoutData(data);
+
+ abiTypeCombo = new Combo(mainComposite, SWT.READ_ONLY);
+ populateAbiTypeCombo(abiTypeCombo);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1);
+ abiTypeCombo.setLayoutData(data);
+
+ abiTypeCombo.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if ((vmSkinCombo != null) && !"".equals(vmSkinCombo.getText())) //$NON-NLS-1$
+ {
+ abiType = (String) abiTypeCombo.getData(abiTypeCombo.getText());
+
+ }
+ else
+ {
+ abiType = SdkConstants.ABI_ARMEABI;
+ }
+ notifyCompositeChangeListeners();
+
+ }
+ });
+
+ targetCombo.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ IAndroidTarget newTarget = null;
+ if ((targetCombo != null) && !"".equals(targetCombo.getText())) //$NON-NLS-1$
+ {
+ newTarget = (IAndroidTarget) targetCombo.getData(targetCombo.getText());
+ }
+
+ if (newTarget != vmTarget)
+ {
+ vmTarget = newTarget;
+ populateSkinCombo(vmSkinCombo);
+ populateAbiTypeCombo(abiTypeCombo);
+ notifyCompositeChangeListeners();
+ }
+ }
+
+ });
+
+ Group vmPathGroup = new Group(mainComposite, SWT.SHADOW_OUT);
+ vmPathGroup.setText(EmulatorNLS.UI_PropertiesMainComposite_PathGroupTitle);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1);
+ vmPathGroup.setLayoutData(data);
+ vmPathGroup.setLayout(new GridLayout(3, false));
+
+ usingDefaultVmPath = vmPath.equals(IDevicePropertiesConstants.defaultVmPath);
+
+ final Button vmPathCheckbox = new Button(vmPathGroup, SWT.CHECK);
+ vmPathCheckbox.setText(EmulatorNLS.UI_PropertiesMainComposite_UseDefaultPath);
+ vmPathCheckbox.setSelection(usingDefaultVmPath);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ vmPathCheckbox.setLayoutData(data);
+
+ Label pathLabel = new Label(vmPathGroup, SWT.READ_ONLY);
+ pathLabel.setText(EmulatorNLS.UI_PropertiesMainComposite_PathLabel);
+ data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ pathLabel.setLayoutData(data);
+
+ final Text pathText = new Text(vmPathGroup, SWT.SINGLE | SWT.BORDER);
+ pathText.setText(vmPath);
+ pathText.setEnabled(!usingDefaultVmPath);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ data.widthHint = 100;
+ pathText.setLayoutData(data);
+
+ final Button pathBrowseButton = new Button(vmPathGroup, SWT.PUSH);
+ pathBrowseButton.setText(EmulatorNLS.UI_General_BrowseButtonLabel);
+ pathBrowseButton.setEnabled(!usingDefaultVmPath);
+ data = new GridData(SWT.FILL, SWT.FILL, false, false);
+ pathBrowseButton.setLayoutData(data);
+
+ vmPathCheckbox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ usingDefaultVmPath = vmPathCheckbox.getSelection();
+ pathText.setEnabled(!usingDefaultVmPath);
+ pathBrowseButton.setEnabled(!usingDefaultVmPath);
+ if (usingDefaultVmPath)
+ {
+ vmPath = IDevicePropertiesConstants.defaultVmPath;
+ }
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ pathText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ vmPath = pathText.getText().trim();
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ pathBrowseButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ DirectoryDialog dirDialog = new DirectoryDialog(getShell(), SWT.OPEN);
+
+ if (!vmPath.trim().equals("")) //$NON-NLS-1$
+ {
+ dirDialog.setFilterPath(vmPath);
+ }
+ else
+ {
+ dirDialog.setFilterPath("/"); //$NON-NLS-1$
+ }
+
+ dirDialog.setText(EmulatorNLS.UI_PropertiesMainComposite_PathGroupTitle);
+
+ String vmPath = dirDialog.open();
+
+ if (vmPath != null)
+ {
+ pathText.setText(vmPath);
+ notifyCompositeChangeListeners();
+ }
+ }
+ });
+
+ /*
+ * SD Card Area
+ */
+
+ sdCardType = SDCARD_TYPE_NONE;
+
+ Group sdCardGroup = new Group(mainComposite, SWT.SHADOW_OUT);
+ sdCardGroup.setText(EmulatorNLS.UI_PropertiesMainComposite_SDCardLabel);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1);
+ sdCardGroup.setLayoutData(data);
+ sdCardGroup.setLayout(new GridLayout(3, false));
+
+ // none
+ final Button noneSDCardCheckbox = new Button(sdCardGroup, SWT.RADIO);
+ noneSDCardCheckbox.setText(EmulatorNLS.UI_PropertiesMainComposite_SDCardNoneLabel);
+ noneSDCardCheckbox.setSelection(true);
+ data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
+ noneSDCardCheckbox.setLayoutData(data);
+ noneSDCardCheckbox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ sdCardType = SDCARD_TYPE_NONE;
+ sdCardValue = ""; //$NON-NLS-1$
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ // existing
+ final Button existingSDCardCheckbox = new Button(sdCardGroup, SWT.RADIO);
+ existingSDCardCheckbox.setText(EmulatorNLS.UI_PropertiesMainComposite_SDCardExistingLabel);
+ final Text existingSDCardPath = new Text(sdCardGroup, SWT.SINGLE | SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ data.widthHint = 100;
+ existingSDCardPath.setLayoutData(data);
+ existingSDCardPath.setEnabled(false);
+ final Button existingSDCardButton = new Button(sdCardGroup, SWT.PUSH);
+ existingSDCardButton.setText(EmulatorNLS.UI_General_BrowseButtonLabel);
+ data = new GridData(SWT.FILL, SWT.FILL, false, false);
+ existingSDCardButton.setLayoutData(data);
+ existingSDCardButton.setEnabled(false);
+
+ existingSDCardCheckbox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ sdCardType = SDCARD_TYPE_PATH;
+ existingSDCardPath.setEnabled(existingSDCardCheckbox.getSelection());
+ existingSDCardButton.setEnabled(existingSDCardCheckbox.getSelection());
+ sdCardValue = existingSDCardPath.getText();
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ existingSDCardPath.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ sdCardValue = existingSDCardPath.getText();
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ existingSDCardButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ String selectedPath = null;
+
+ FileDialog fileDialog = new FileDialog(getShell(), SWT.OPEN);
+ String[] filterExtensions =
+ {
+ "*" + SDCARD_PATH_EXTENSION //$NON-NLS-1$
+ };
+
+ fileDialog.setFilterExtensions(filterExtensions);
+ selectedPath = fileDialog.open();
+
+ if (selectedPath != null)
+ {
+ existingSDCardPath.setText(selectedPath);
+ }
+ }
+ });
+
+ // new
+ final Button newSDCardCheckbox = new Button(sdCardGroup, SWT.RADIO);
+ newSDCardCheckbox.setText(EmulatorNLS.UI_PropertiesMainComposite_SDCardNewLabel);
+ final Text newSDCardSize = new Text(sdCardGroup, SWT.SINGLE | SWT.BORDER);
+ data = new GridData(SWT.FILL, SWT.TOP, true, false);
+ data.widthHint = 100;
+ newSDCardSize.setLayoutData(data);
+ newSDCardSize.setEnabled(false);
+ final Combo newSDCardUnit = new Combo(sdCardGroup, SWT.READ_ONLY);
+ for (String unit : SDCARD_SIZE_UNITS)
+ {
+ newSDCardUnit.add(unit);
+ }
+ newSDCardUnit.select(0);
+ newSDCardUnit.setEnabled(false);
+ data = new GridData(SWT.FILL, SWT.FILL, false, false);
+ newSDCardUnit.setLayoutData(data);
+
+ newSDCardCheckbox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ sdCardType = SDCARD_TYPE_SIZE;
+ sdCardValue = newSDCardSize.getText();
+ newSDCardSize.setEnabled(newSDCardCheckbox.getSelection());
+ newSDCardUnit.setEnabled(newSDCardCheckbox.getSelection());
+ sdCardValue = newSDCardSize.getText() + newSDCardUnit.getText().charAt(0);
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ ModifyListener newSDCardListener = new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ sdCardValue = newSDCardSize.getText() + newSDCardUnit.getText().charAt(0);
+ notifyCompositeChangeListeners();
+ }
+ };
+
+ newSDCardSize.addModifyListener(newSDCardListener);
+ newSDCardUnit.addModifyListener(newSDCardListener);
+ }
+
+ private void populateAbiTypeCombo(Combo abiTypeCombo)
+ {
+ // System Images represents the ABI types
+ ISystemImage[] images = vmTarget.getSystemImages();
+
+ // in case no images are found, get try its parent
+ if ((images == null) || ((images.length == 0) && !vmTarget.isPlatform()))
+ {
+ images = vmTarget.getParent().getSystemImages();
+ }
+
+ // always clean abi combo since it will be reloaded
+ abiTypeCombo.removeAll();
+
+ int i = 0;
+ if ((images != null) && (images.length > 0))
+ {
+ for (ISystemImage image : images)
+ {
+ String prettyAbiName = AvdInfo.getPrettyAbiType(image.getAbiType());
+ abiTypeCombo.add(prettyAbiName);
+ abiTypeCombo.setData(prettyAbiName, image.getAbiType());
+ if (image.getAbiType().equals(abiType))
+ {
+ abiTypeCombo.select(i);
+ }
+ i++;
+ }
+
+ if (abiTypeCombo.getSelectionIndex() == -1)
+ {
+ abiTypeCombo.select(0);
+ abiType = (String) abiTypeCombo.getData(abiTypeCombo.getItem(0));
+ }
+ }
+
+ }
+
+ /**
+ * Populate VM Target combo box.
+ *
+ * @param targetCombo
+ */
+ private void populateTargetCombo(Combo targetCombo)
+ {
+ IAndroidTarget[] targets = SdkUtils.getAllTargets();
+ if ((targets != null) && (targets.length > 0))
+ {
+ for (int i = 0; i < targets.length; i++)
+ {
+ String label =
+ targets[i].isPlatform() ? targets[i].getName() : targets[i].getName()
+ + " (" + targets[i].getParent().getName() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+ targetCombo.add(label);
+ targetCombo.setData(label, targets[i]);
+
+ if (targets[i].getName().equals(vmTarget.getName()))
+ {
+ targetCombo.select(i);
+ }
+ }
+ }
+ //Set context Help (not available yet)
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(this, IAndroidDeviceConstants.MAIN_PAGE_HELP);
+ }
+
+ /**
+ * Populate VM Skin combo box.
+ *
+ * @param skinCombo
+ */
+ private void populateSkinCombo(Combo skinCombo)
+ {
+ skinCombo.removeAll();
+ skinCombo.clearSelection();
+
+ if (vmTarget != null)
+ {
+ String[] skins = vmTarget.getSkins();
+ String defaultSkin = vmTarget.getDefaultSkin();
+
+ for (int i = 0; i < skins.length; i++)
+ {
+ skinCombo.add(skins[i]);
+ skinCombo.setData(skins[i], skins[i]);
+
+ if (skins[i].equals(defaultSkin))
+ {
+ skinCombo.select(i);
+ vmSkin = defaultSkin;
+ }
+ }
+
+ // if there is no selection, select the first
+ if (skinCombo.getSelectionIndex() < 0)
+ {
+ if (skinCombo.getItemCount() > 0)
+ {
+ skinCombo.select(0);
+ vmSkin = skinCombo.getItem(0);
+ }
+ }
+
+ skinCombo.setEnabled(true);
+ }
+ else
+ {
+ skinCombo.setEnabled(false);
+ }
+
+ //Set context Help (not available yet)
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(this, IAndroidDeviceConstants.MAIN_PAGE_HELP);
+ }
+
+ public String getSkinId()
+ {
+ return skinId;
+ }
+
+ /**
+ * Retrieves the timeout.
+ *
+ * @return the timeout
+ */
+ public String getTimeout()
+ {
+ return timeout;
+ }
+
+ public String getUseVnc()
+ {
+ return (useVnc ? Boolean.TRUE.toString() : Boolean.FALSE.toString()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public String getUseProxy()
+ {
+ return (useProxy ? Boolean.TRUE.toString() : Boolean.FALSE.toString()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Retrieves the error message associated to this composites current state.
+ * The order of precedence of error is the same as the fields displayed on the
+ * UI, which means errors on fields drawn first are shown with a higher precedence
+ * than the errors of fields drawn last.
+ * The instance description field is the only non required field.
+ *
+ * @return the error message, or <code>null</code> if there are no errors
+ */
+ @Override
+ public String getErrorMessage()
+ {
+ String errorMessage = null;
+
+ // VM Settings Check
+ if ("".equals(vmTarget)) //$NON-NLS-1$
+ {
+ //Check if Target isn't empty
+ errorMessage = EmulatorNLS.ERR_PropertiesMainComposite_VmTargetEmpty;
+ }
+ else if ("".equals(vmSkin)) //$NON-NLS-1$
+ {
+ //Check if Skin isn't empty
+ errorMessage = EmulatorNLS.ERR_PropertiesMainComposite_VmSkinEmpty;
+ }
+ else
+ {
+ if (!usingDefaultVmPath)
+ {
+ //Check if Path is valid ("" is valid too)
+ File vmPathLocation = new File(vmPath);
+ if ((!vmPathLocation.exists()) || (!vmPathLocation.isDirectory()))
+ {
+ errorMessage = EmulatorNLS.ERR_PropertiesMainComposite_VmPathInvalid;
+ }
+
+ }
+ }
+
+ //ABI Type
+ if (errorMessage == null)
+ {
+ if ((abiTypeCombo != null)
+ && ((abiTypeCombo.getItemCount() == 0) || (abiTypeCombo.getSelectionIndex() == -1))) //$NON-NLS-1$
+ {
+ //no item available or not ABI selected
+ errorMessage = EmulatorNLS.ERR_PropertiesMainComposite_ABINotAvailable;
+ }
+ }
+
+ // SD Card
+ if (errorMessage == null)
+ {
+ if (sdCardType.equalsIgnoreCase(SDCARD_TYPE_PATH))
+ {
+ if (sdCardValue == null) //$NON-NLS-1$
+ {
+ errorMessage = EmulatorNLS.ERR_PropertiesMainComposite_MissingSDCardPath;
+ }
+ else if (!isValidSDCard(sdCardValue))
+ {
+ errorMessage = EmulatorNLS.ERR_PropertiesMainComposite_SDCardPathIsNotValid;
+ }
+ }
+ else if (sdCardType.equalsIgnoreCase(SDCARD_TYPE_SIZE))
+ {
+
+ int sdcardSize = -1;
+ String unit =
+ sdCardValue.length() > 0 ? sdCardValue.substring(sdCardValue.length() - 1)
+ .toLowerCase() : "k"; //$NON-NLS-1$
+
+ if ((sdCardValue == null) || (sdCardValue.equals(""))) //$NON-NLS-1$
+ {
+ errorMessage = EmulatorNLS.ERR_PropertiesMainComposite_MissingSDCardSize;
+ }
+ else
+ {
+
+ try
+ {
+ sdcardSize =
+ Integer.parseInt(sdCardValue.substring(0, sdCardValue.length() - 1));
+ }
+ catch (NumberFormatException e)
+ {
+ //do nothing
+ }
+
+ if ((unit.equals("m") && (sdcardSize < 9)) //$NON-NLS-1$
+ || (unit.equals("k") && (sdcardSize < (9 * 1024)))) //$NON-NLS-1$
+ {
+ errorMessage =
+ EmulatorNLS.ERR_PropertiesMainComposite_SDCardSizeIsNotPositiveInteger;
+ }
+ }
+ }
+ }
+
+ //Timeout
+ if (errorMessage == null)
+ {
+ if (timeout.equals("")) //$NON-NLS-1$
+ {
+ errorMessage = EmulatorNLS.ERR_PropertiesMainComposite_MissingTimeoutValue;
+ }
+ else
+ {
+ if (!isPositiveInteger(timeout))
+ {
+ errorMessage =
+ EmulatorNLS.ERR_PropertiesMainComposite_TimeoutValueIsNotPositiveInteger;
+ }
+ }
+ }
+
+ return errorMessage;
+ }
+
+ /**
+ * Check if a string is a valid SD Card Path
+ *
+ * @param text the string to be analyzed
+ * @return true if the string is a valid SD Card path, false otherwise
+ */
+ private boolean isValidSDCard(String text)
+ {
+ boolean result = true;
+
+ File file = new File(text);
+
+ if ((!file.exists()) || (file.isDirectory())
+ || (!SDCARD_PATH_EXTENSION.equals("." + (new Path(text)).getFileExtension()))) //$NON-NLS-1$
+ {
+ result = false;
+ }
+
+ return result;
+ }
+
+ /**
+ * Check if a string is a positive integer
+ *
+ * @param text the string to be analyzed
+ * @return true if the string is a positive integer, false otherwise
+ */
+ private boolean isPositiveInteger(String text)
+ {
+ int intValue = 0;
+ boolean isPositive = true;
+
+ try
+ {
+ intValue = Integer.parseInt(text);
+ }
+ catch (NumberFormatException e)
+ {
+ isPositive = false;
+ }
+
+ if (intValue <= 0)
+ {
+ isPositive = false;
+ }
+
+ return isPositive;
+ }
+
+ /**
+ * Retrieves the VM Target.
+ *
+ * @return the vmTarget
+ */
+ public IAndroidTarget getVmTarget()
+ {
+ return vmTarget;
+ }
+
+ /**
+ * Retrieves the Abi Type.
+ *
+ * @return the VM Abi Type
+ */
+ public String getAbiType()
+ {
+ return abiType != null ? abiType : SdkConstants.ABI_ARMEABI;
+ }
+
+ /**
+ * Retrieves the VM Skin.
+ *
+ * @return the vmSkin
+ */
+ public String getVmSkin()
+ {
+ return vmSkin;
+ }
+
+ /**
+ * Retrieves the VM Path.
+ *
+ * @return the vmPath
+ */
+ public String getVmPath()
+ {
+ return vmPath + File.separator + name + IDevicePropertiesConstants.defaultVmFolderSuffix;
+ }
+
+ /**
+ * Retrieves the SD Card info
+ *
+ * @return the sdCard
+ */
+ public String getSDCard()
+ {
+ return sdCardValue;
+ }
+
+ /**
+ * Set the name of the AVD
+ * @param name: a not null String with the name of the AVD
+ */
+ public void setName(String name)
+ {
+ this.name = name == null ? "" : name; //$NON-NLS-1$
+ }
+
+ /**
+ * @return
+ */
+ public String getUseSnapshot()
+ {
+ return (useSnapshots ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
+ }
+
+ /**
+ * @return
+ */
+ public String getstartFromSnapshot()
+ {
+ return (startFromSnapshots ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
+ }
+
+ /**
+ * @return
+ */
+ public String getSaveSnapshot()
+ {
+ return (saveSnapshots ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/StartupOptionsComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/StartupOptionsComposite.java
new file mode 100644
index 0000000..b6f3f9c
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/StartupOptionsComposite.java
@@ -0,0 +1,435 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.ui;
+
+import java.util.List;
+
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.fieldassist.ControlDecoration;
+import org.eclipse.jface.fieldassist.FieldDecoration;
+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.device.IAndroidDeviceConstants;
+import com.motorola.studio.android.emulator.device.instance.options.IStartupOptionsConstants;
+import com.motorola.studio.android.emulator.device.instance.options.StartupOption;
+import com.motorola.studio.android.emulator.device.instance.options.StartupOptionsGroup;
+import com.motorola.studio.android.emulator.device.instance.options.StartupOptionsMgt;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class implements the UI for showing all Android Emulator Device Instance startup options information.
+ * <br>
+ * It extends the AbstractPropertiesComposite so as to use its common functionalities.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Show Android Emulator Device Instance main information on the UI
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * AbstractPropertiesComposite: extends this class
+ * <br>
+ * USAGE:
+ * <br>
+ * This class should be added as a regular composite whenever startup options information on Android Emulator
+ * Device Instance is necessary to be shown and edited on the UI.
+ */
+public class StartupOptionsComposite extends AbstractPropertiesComposite implements
+ IStartupOptionsConstants
+{
+ // The widget which displays the current command line used to pass the startup options
+ private Text commandLine;
+
+ private final IAndroidSkin skin;
+
+ private final int TABFOLDER_HEIGHT_HINT = 350;
+
+ private boolean canCalculateScale = true;
+
+ /**
+ * Creates a StartupOptionsComposite object.
+ *
+ * @param parent the parent composite
+ * @param canCalculateScale
+ */
+ public StartupOptionsComposite(Composite parent, String startupOptions, IAndroidSkin skin,
+ boolean canCalculateScale)
+ {
+ super(parent);
+
+ this.skin = skin;
+ this.canCalculateScale = canCalculateScale;
+ StartupOptionsMgt.loadFromCommandLine(startupOptions);
+ createUI();
+
+ // Set context Help
+ PlatformUI.getWorkbench().getHelpSystem()
+ .setHelp(parent, IAndroidDeviceConstants.STARTUP_OPTIONS_HELP);
+ }
+
+ /**
+ * Create widgets for startup options
+ */
+ private void createUI()
+ {
+
+ Composite mainComposite = this;
+ Layout mainLayout = new GridLayout();
+ mainComposite.setLayout(mainLayout);
+
+ // list of startup options groups
+ List<StartupOptionsGroup> startupOptionsGroupsList =
+ StartupOptionsMgt.getStartupOptionsGroupsList();
+
+ // list of startup options in each group
+ List<StartupOption> startupOptions = null;
+
+ // Create Tab Folder
+ final TabFolder tabFolder = new TabFolder(mainComposite, SWT.NULL);
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, false);
+ data.heightHint = TABFOLDER_HEIGHT_HINT;
+ tabFolder.setLayoutData(data);
+
+ /*
+ * Iterate through Startup Groups
+ */
+ for (StartupOptionsGroup startupOptionGroup : startupOptionsGroupsList)
+ {
+
+ // Create Tab Item
+ TabItem tabItem = new TabItem(tabFolder, SWT.NULL);
+ tabItem.setText(startupOptionGroup.getTitle());
+ Composite group = new Composite(tabFolder, SWT.NULL);
+ group.setLayout(new GridLayout(3, false));
+ group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ tabItem.setControl(group);
+
+ // get startup options in this group
+ startupOptions = startupOptionGroup.getStartupOptions();
+
+ /*
+ * Iterate through Startup Options in this group
+ */
+ for (final StartupOption startupOption : startupOptions)
+ {
+
+ // create a checkbox for each startup option
+ Button checkbox = new Button(group, SWT.CHECK);
+ checkbox.setSelection(startupOption.isChecked());
+ checkbox.setText(startupOption.getUserFriendlyName());
+ checkbox.setToolTipText(startupOption.getName() + ": " //$NON-NLS-1$
+ + startupOption.getDescription());
+ startupOption.setCheckedWidget(checkbox);
+ checkbox.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ boolean checkedStatus = ((Button) e.widget).getSelection();
+ startupOption.setChecked(checkedStatus);
+ notifyCompositeChangeListeners();
+ }
+ });
+ GridData checkboxData = new GridData(SWT.NULL, SWT.FILL, false, false);
+ checkbox.setLayoutData(checkboxData);
+
+ // Create input fields depending on the startup option type
+ switch (startupOption.getType())
+ {
+ case TYPE_NONE:
+ // extend checkbox area along the line
+ checkboxData.widthHint = SWT.DEFAULT;
+ checkboxData.horizontalSpan = 3;
+ checkbox.setLayoutData(checkboxData);
+ break;
+
+ case TYPE_TEXT:
+ case TYPE_NUMBER:
+ createWidgetsForTextOrNumberType(group, startupOption);
+ break;
+
+ case TYPE_PATH:
+ createWidgetsForPathType(group, startupOption);
+ break;
+
+ default:
+ // none
+
+ }
+ }
+ }
+
+ /*
+ * Command Line area
+ */
+ Composite commandLineArea = new Composite(mainComposite, SWT.NONE); // composite
+ commandLineArea.setLayout(new GridLayout(2, false));
+ data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ commandLineArea.setLayoutData(data);
+
+ Label commandLineLabel = new Label(commandLineArea, SWT.NONE); // label
+ commandLineLabel.setText(""); //$NON-NLS-1$
+ data = new GridData(SWT.FILL, SWT.FILL, false, true);
+ commandLineLabel.setLayoutData(data);
+
+ commandLine = new Text(commandLineArea, SWT.MULTI | SWT.WRAP | SWT.BORDER | SWT.V_SCROLL); // text
+ data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ commandLineArea.pack();
+ data.widthHint = commandLineArea.getBounds().width - commandLineLabel.getBounds().width;
+ data.heightHint = commandLineArea.getBounds().height;
+ commandLine.setLayoutData(data);
+ commandLine.setText(StartupOptionsMgt.getParamList());
+ commandLine.setEditable(false);
+ }
+
+ /**
+ * Create widgets to enable user to input data for fields of text or number type
+ *
+ * @param parent composite where the widgets will be attached to
+ * @param startupOption the corresponding startup option
+ */
+ private void createWidgetsForTextOrNumberType(final Composite parent,
+ final StartupOption startupOption)
+ {
+ // create input text with calc button
+ if (startupOption.getName().equals(SCALE))
+ {
+ final Text inputText = new Text(parent, SWT.SINGLE | SWT.BORDER);
+ inputText.setText(startupOption.getValue());
+ startupOption.setValueWidget(inputText);
+ inputText.setLayoutData(new GridData(SWT.FILL, SWT.NULL, true, false));
+ inputText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ startupOption.setValue(inputText.getText());
+ notifyCompositeChangeListeners();
+ }
+ });
+
+ Button calc = new Button(parent, SWT.PUSH);
+ calc.setText(EmulatorNLS.UI_DpiScale_Calculator);
+ GridData calcData = new GridData(SWT.NULL, SWT.NULL, false, false);
+ calc.setLayoutData(calcData);
+ calc.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ DpiScaleCalculatorDialog dialog =
+ new DpiScaleCalculatorDialog(new Shell(parent.getShell()), skin);
+ if (dialog.open() == Dialog.OK)
+ {
+ for (StartupOptionsGroup group : StartupOptionsMgt
+ .getStartupOptionsGroupsList())
+ {
+ for (StartupOption startupOption : group.getStartupOptions())
+ {
+ if (startupOption.getName().equals(SCALE))
+ {
+ startupOption.setChecked(true);
+ startupOption.setValue(dialog.getResultScaleValue());
+ startupOption.updateUI();
+ }
+ }
+ }
+ }
+ }
+ });
+ calc.setEnabled(canCalculateScale);
+ if (!canCalculateScale)
+ {
+ ControlDecoration controlDecoration =
+ new ControlDecoration(inputText, SWT.LEFT | SWT.TOP);
+ controlDecoration
+ .setDescriptionText(EmulatorNLS.StartupOptionsComposite_Error_Loading_Skin_Cant_Calculate_Scale);
+ FieldDecoration fieldDecoration =
+ FieldDecorationRegistry.getDefault().getFieldDecoration(
+ FieldDecorationRegistry.DEC_WARNING);
+ controlDecoration.setImage(fieldDecoration.getImage());
+ }
+ }
+ // create input text
+ else if ((startupOption.getPreDefinedValues() == null)
+ || (startupOption.getPreDefinedValues().size() == 0))
+ {
+ final Text inputText = new Text(parent, SWT.SINGLE | SWT.BORDER);
+ inputText.setText(startupOption.getValue());
+ startupOption.setValueWidget(inputText);
+ inputText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+ inputText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ startupOption.setValue(inputText.getText());
+ notifyCompositeChangeListeners();
+ }
+ });
+ }
+ // create combobox
+ else
+ {
+ final Combo combo = new Combo(parent, SWT.READ_ONLY);
+ startupOption.setValueWidget(combo);
+ int selectedIndex = 0;
+ for (String preDefinedValue : startupOption.getPreDefinedValues())
+ {
+ combo.add(preDefinedValue);
+ if (startupOption.getValue().equals(preDefinedValue))
+ {
+ combo.select(selectedIndex);
+ }
+ else
+ {
+ selectedIndex++;
+ }
+ }
+ combo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+ combo.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ startupOption.setValue(combo.getText());
+ notifyCompositeChangeListeners();
+ }
+ });
+ }
+ }
+
+ /**
+ * Create widgets to enable user to input data for fields of path type
+ *
+ * @param parent composite where the widgets will be attached to
+ * @param startupOption the corresponding startup option
+ */
+ private void createWidgetsForPathType(final Composite parent, final StartupOption startupOption)
+ {
+ // create input text
+ final Text pathText = new Text(parent, SWT.SINGLE | SWT.BORDER);
+ pathText.setText(startupOption.getValue());
+ startupOption.setValueWidget(pathText);
+ pathText.setLayoutData(new GridData(SWT.FILL, SWT.NULL, true, false));
+ pathText.addModifyListener(new ModifyListener()
+ {
+ public void modifyText(ModifyEvent e)
+ {
+ startupOption.setValue(pathText.getText());
+ notifyCompositeChangeListeners();
+ }
+ });
+ // create browse button
+ Button pathBrowseButton = new Button(parent, SWT.PUSH);
+ pathBrowseButton.setText(EmulatorNLS.UI_General_BrowseButtonLabel);
+ GridData data = new GridData(SWT.NULL, SWT.NULL, false, false);
+ pathBrowseButton.setLayoutData(data);
+ pathBrowseButton.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ String selectedPath = null;
+
+ if (startupOption.getTypeDetails().equalsIgnoreCase(TYPE_PATH_DIR))
+ {
+ DirectoryDialog directoryDialog = new DirectoryDialog(getShell(), SWT.OPEN);
+ selectedPath = directoryDialog.open();
+ }
+ else
+ {
+ FileDialog fileDialog = new FileDialog(getShell(), SWT.OPEN);
+ String[] filterExtensions =
+ {
+ "*" + startupOption.getTypeDetails() //$NON-NLS-1$
+ };
+ fileDialog.setFilterExtensions(filterExtensions);
+ selectedPath = fileDialog.open();
+ }
+
+ if (selectedPath != null)
+ {
+ pathText.setText(selectedPath);
+ }
+ }
+ });
+ }
+
+ /**
+ * Update command line value
+ *
+ * @see com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite#notifyCompositeChangeListeners()
+ */
+ @Override
+ protected void notifyCompositeChangeListeners()
+ {
+ commandLine.setText(StartupOptionsMgt.getParamList());
+ super.notifyCompositeChangeListeners();
+ }
+
+ /**
+ * Retrieves the error message associated to this composites current state.
+ * The order of precedence of error is the same as the fields displayed on the
+ * UI, which means errors on fields drawn first are shown with a higher precedence
+ * than the errors of fields drawn last.
+ * The instance description field is the only non required field.
+ *
+ * @return the error message, or <code>null</code> if there are no errors
+ */
+ @Override
+ public String getErrorMessage()
+ {
+ String errMsg = null;
+ Status status = StartupOptionsMgt.validate();
+ if (status.getSeverity() == Status.ERROR)
+ {
+ errMsg = status.getMessage();
+ }
+ return errMsg;
+ }
+
+ /**
+ * Reload the values being displayed in the UI as well as the ones
+ * in the model.
+ *
+ * @param startupOptions commandLine the command line used to start the emulator
+ */
+ public void reloadValues(String commandLine)
+ {
+ StartupOptionsMgt.loadFromCommandLine(commandLine);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardMainPage.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardMainPage.java
new file mode 100644
index 0000000..35f7457
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardMainPage.java
@@ -0,0 +1,445 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.ui.wizard;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Properties;
+
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.sequoyah.device.framework.ui.wizard.DefaultDeviceTypeMenuWizardPage;
+import org.eclipse.sequoyah.device.framework.ui.wizard.IInstanceProperties;
+import org.eclipse.swt.widgets.Composite;
+
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite.PropertyCompositeChangeEvent;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite.PropertyCompositeChangeListener;
+import com.motorola.studio.android.emulator.device.ui.PropertiesMainComposite;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class represents the first wizard page for Android Emulator Device Instance creation.
+ * <br>
+ * It shows all main information and validates it, setting an appropriate error message when
+ * applicable.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Allow user to enter main information for creating a new Android Emulator Device Instance
+ * <br>
+ * - Validate main information entered by user
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * WizardPage: extends this class
+ * <br>
+ * PropertiesMainComposite: uses this composite as the main widget
+ * <br>
+ * USAGE:
+ * <br>
+ * This wizard page must be added as the first page on the class implementing the New Android
+ * Emulator Device Instance Wizard.
+ */
+public class WizardMainPage extends WizardPage implements IInstanceProperties
+{
+ private PropertiesMainComposite mainComposite;
+
+ DefaultDeviceTypeMenuWizardPage tmlPage = null;
+
+ private PropertyCompositeChangeListener listener = new PropertyCompositeChangeListener()
+ {
+ public void compositeChanged(PropertyCompositeChangeEvent e)
+ {
+
+ String errorMessage = mainComposite.getErrorMessage();
+
+ if (errorMessage != null)
+ {
+ setErrorMessage(errorMessage);
+ setPageComplete(false);
+ }
+ else
+ {
+ setErrorMessage(null);
+ setPageComplete(true);
+ setMessage(EmulatorNLS.UI_WizardMainPage_PageName);
+ }
+
+ }
+ };
+
+ /**
+ * Creates a WizardMainPage object.
+ */
+ public WizardMainPage()
+ {
+ super(EmulatorNLS.UI_WizardMainPage_PageName);
+ }
+
+ /**
+ * Creates the UI for this wizard page.
+ * It uses the PropertiesMainComposite only.
+ */
+ public void createControl(Composite parent)
+ {
+
+ // Collecting usage data for statistical purpose
+ try
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_EMULATOR_CREATION_WIZARD,
+ StudioLogger.KIND_EMULATOR, StudioLogger.DESCRIPTION_DEFAULT,
+ EmulatorPlugin.PLUGIN_ID, EmulatorPlugin.getDefault().getBundle().getVersion()
+ .toString());
+ }
+ catch (Throwable e)
+ {
+ //Do nothing, but error on the log should never prevent app from working
+ }
+
+ setTitle(EmulatorNLS.UI_General_WizardTitle);
+ setErrorMessage(null);
+ setMessage(EmulatorNLS.UI_WizardMainPage_PageName);
+
+ IAndroidTarget vmTarget = null;
+ String vmSkin = ""; //$NON-NLS-1$
+ String vmPath;
+ String timeout;
+ String useVnc;
+ String useProxy;
+ String abiType = SdkConstants.ABI_ARMEABI;
+ String useSnapshot;
+ String saveSnapshot;
+ String startFromSnapshot;
+
+ tmlPage = (DefaultDeviceTypeMenuWizardPage) this.getPreviousPage();
+
+ IAndroidTarget targets[] = SdkUtils.getAllTargets();
+
+ if ((targets != null) && (targets.length > 0))
+ {
+
+ // Sort the targets array by comparing the API level of them
+ Arrays.sort(targets, new Comparator<IAndroidTarget>()
+ {
+
+ public int compare(IAndroidTarget o1, IAndroidTarget o2)
+ {
+ int returnValue;
+ if (o1.getVersion().getApiLevel() == o2.getVersion().getApiLevel())
+ {
+ returnValue = 0;
+ }
+ else if (o1.getVersion().getApiLevel() > o2.getVersion().getApiLevel())
+ {
+ returnValue = 1;
+ }
+ else
+ {
+ returnValue = -1;
+ }
+
+ return returnValue;
+ }
+
+ });
+
+ // Gets the first target with the highest API
+
+ int maxAPILevel = targets[targets.length - 1].getVersion().getApiLevel();
+
+ for (IAndroidTarget t : targets)
+ {
+ if (t.getVersion().getApiLevel() == maxAPILevel)
+ {
+ vmTarget = t;
+ break;
+ }
+
+ }
+
+ String skins[] = vmTarget.getSkins();
+ vmSkin = vmTarget.getDefaultSkin();
+ List<String> skinsList = Arrays.asList(skins);
+
+ /*
+ * Workaround to select WVGA skin on JIL SDK because HVGA skin is broken
+ */
+ if (SdkUtils.isJILSdk())
+ {
+ String tmpVmSkin = null;
+ int i = 0;
+ while ((tmpVmSkin == null) && (i < skins.length))
+ {
+ if (skins[i].toLowerCase().trim().equals("wvga"))
+ {
+ tmpVmSkin = skins[i];
+ }
+ i++;
+ }
+ if (tmpVmSkin != null)
+ {
+ vmSkin = tmpVmSkin;
+ }
+ }
+
+ if (!skinsList.contains(vmSkin))
+ {
+ vmSkin = skins[0];
+ }
+ }
+
+ vmPath = IDevicePropertiesConstants.defaultVmPath;
+
+ // get the default properties value
+ Properties defaultProperties = new Properties();
+ AndroidDeviceInstance.populateWithDefaultProperties(defaultProperties);
+ timeout = defaultProperties.getProperty(IDevicePropertiesConstants.timeout);
+ useVnc = defaultProperties.getProperty(IDevicePropertiesConstants.useVnc);
+ useProxy = defaultProperties.getProperty(IDevicePropertiesConstants.useProxy);
+ useSnapshot = defaultProperties.getProperty(IDevicePropertiesConstants.useSnapshots);
+ saveSnapshot = defaultProperties.getProperty(IDevicePropertiesConstants.saveSnapshot);
+ startFromSnapshot = defaultProperties.getProperty(IDevicePropertiesConstants.startFromSnapshot);
+
+ // When removing the emulator definition extension, remove this hardcoded set as
+ // well as the constant declaration from the Activator. If the Mot skin plugin is used,
+ // find another way to set this variable
+ mainComposite =
+ new PropertiesMainComposite(parent, tmlPage.getInstanceName(),
+ EmulatorPlugin.DEFAULT_EMULATOR_DEFINITION, timeout,
+ Boolean.parseBoolean(useVnc), Boolean.parseBoolean(useProxy),
+ Boolean.parseBoolean(useSnapshot), Boolean.parseBoolean(saveSnapshot),
+ Boolean.parseBoolean(startFromSnapshot), vmTarget, vmSkin, vmPath, abiType,
+ false, true, true);
+
+ AbstractPropertiesComposite.addCompositeChangeListener(listener);
+
+ setControl(mainComposite);
+
+ if ((targets == null) || ((targets != null) && (targets.length <= 0)))
+ {
+ setMessage(EmulatorNLS.WizardMainPage_NO_SDK_CONFIGURED_MSG);
+ setPageComplete(false);
+ return;
+ }
+
+ String initialMessage = mainComposite.getErrorMessage();
+
+ if (initialMessage != null)
+ {
+ setMessage(initialMessage);
+ }
+ setPageComplete(initialMessage == null);
+ }
+
+ @Override
+ public void setVisible(boolean visible)
+ {
+ if (visible)
+ {
+ mainComposite.setName(tmlPage.getInstanceName());
+ }
+ super.setVisible(visible);
+ }
+
+ /**
+ * Retrieves the skin id associated with this instance
+ *
+ * @return the skin id
+ */
+ public String getSkinId()
+ {
+ return mainComposite.getSkinId();
+ }
+
+ /**
+ * Retrieves the timeout associated with this instance
+ *
+ * @return the timeout
+ */
+ public String getTimeout()
+ {
+ return mainComposite.getTimeout();
+ }
+
+ /**
+ * Retrieves the vnc option associated with this instance
+ *
+ * @return the timeout
+ */
+ public String getUseVnc()
+ {
+ return mainComposite.getUseVnc();
+ }
+
+ /**
+ * Retrieves the proxy option associated with this instance
+ *
+ * @return the timeout
+ */
+ public String getUseProxy()
+ {
+ return mainComposite.getUseProxy();
+ }
+
+ /**
+ * Retrieves the emulator definition name associated with this instance
+ *
+ * @return the emulator definition name
+ */
+ public String getEmulatorDefId()
+ {
+ // When removing the emulator definition extension, remove this hardcoded set as
+ // well as the constant declaration from the Activator. If the Mot skin plugin is used,
+ // find another way to set this variable
+ return EmulatorPlugin.DEFAULT_EMULATOR_DEFINITION;
+ }
+
+ public String getUseSnapshot()
+ {
+ return mainComposite.getUseSnapshot();
+ }
+
+ public void setInstanceName(String name)
+ {
+ mainComposite.setName(name);
+ }
+
+ /**
+ * Retrieves VM target
+ */
+ public IAndroidTarget getVmTarget()
+ {
+ return mainComposite.getVmTarget();
+ }
+
+ /**
+ * Retrieves VM target
+ */
+ public String getAbiType()
+ {
+ return mainComposite.getAbiType();
+ }
+
+ /**
+ * Retrieves VM skin
+ */
+ public String getVmSkin()
+ {
+ return mainComposite.getVmSkin();
+ }
+
+ /**
+ * Retrieves VM path
+ */
+ public String getVmPath()
+ {
+ return mainComposite.getVmPath();
+ }
+
+ /**
+ * Retrieves SD Card info
+ */
+ public String getSDCard()
+ {
+ return mainComposite.getSDCard();
+ }
+
+ /**
+ * Retrieves Command line
+ */
+ public String getCommandLine()
+ {
+ //The command line shall be editable through the GUI
+ Properties defaultProperties = new Properties();
+ AndroidDeviceInstance.populateWithDefaultProperties(defaultProperties);
+ return defaultProperties.getProperty(IDevicePropertiesConstants.commandline);
+ }
+
+ @Override
+ public boolean isPageComplete()
+ {
+ return (mainComposite != null) && (mainComposite.getErrorMessage() == null);
+ }
+
+ @Override
+ public void dispose()
+ {
+ AbstractPropertiesComposite.removeCompositeChangeListener(listener);
+ setControl(null);
+ if (mainComposite != null)
+ {
+ mainComposite.dispose();
+ mainComposite = null;
+ }
+
+ super.dispose();
+ }
+
+ public Properties getProperties()
+ {
+ Properties properties = new Properties();
+
+ properties.setProperty(IDevicePropertiesConstants.timeout, this.getTimeout());
+ properties.setProperty(IDevicePropertiesConstants.useVnc, this.getUseVnc());
+ properties.setProperty(IDevicePropertiesConstants.useProxy, this.getUseProxy());
+ properties.setProperty(IDevicePropertiesConstants.emulatorDefId, this.getEmulatorDefId());
+ properties.setProperty(IDevicePropertiesConstants.skinId, this.getSkinId());
+ properties.setProperty(IDevicePropertiesConstants.vmSkin, this.getVmSkin());
+ properties.setProperty(IDevicePropertiesConstants.vmPath, this.getVmPath());
+ properties.setProperty(IDevicePropertiesConstants.abiType, this.getAbiType());
+ properties.setProperty(IDevicePropertiesConstants.useSnapshots, this.getUseSnapshot());
+ properties.setProperty(IDevicePropertiesConstants.saveSnapshot, this.getSaveSnapshot());
+ properties.setProperty(IDevicePropertiesConstants.startFromSnapshot, this.getstartFromSnapshot());
+
+ if (this.getVmTarget() != null)
+ {
+ properties.setProperty(IDevicePropertiesConstants.vmTarget, this.getVmTarget()
+ .getName());
+ }
+ else
+ {
+ properties.setProperty(IDevicePropertiesConstants.vmTarget, ""); //$NON-NLS-1$
+ }
+
+ return properties;
+ }
+
+ /**
+ * @return
+ */
+ public String getstartFromSnapshot()
+ {
+ return mainComposite.getstartFromSnapshot();
+ }
+
+ /**
+ * @return
+ */
+ public String getSaveSnapshot()
+ {
+ return mainComposite.getSaveSnapshot();
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardMainPageOperation.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardMainPageOperation.java
new file mode 100644
index 0000000..772684c
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardMainPageOperation.java
@@ -0,0 +1,76 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.ui.wizard;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collection;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.sequoyah.device.framework.ui.wizard.DefaultDeviceTypeMenuWizardPage;
+import org.eclipse.sequoyah.device.framework.ui.wizard.DeviceWizardRunnable;
+
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.device.refresh.InstancesListRefresh;
+
+/**
+ * This class performs the wizard finish operation for the WizardMainPage,
+ * according to the extension point
+ * org.eclipse.sequoyah.device.framework.ui.newDeviceWizardPages
+ */
+public class WizardMainPageOperation extends DeviceWizardRunnable
+{
+ /**
+ * Action executed on the wizard finish
+ */
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException
+ {
+ // Get wizard pages
+ WizardMainPage page = (WizardMainPage) this.getWizardPage();
+ DefaultDeviceTypeMenuWizardPage tmlPage =
+ (DefaultDeviceTypeMenuWizardPage) page.getPreviousPage();
+
+ // Create VM
+ try
+ {
+ //TML should provide some instance name changed listener
+ if (!tmlPage.getInstanceName().equals(page.getName()))
+ {
+ page.setInstanceName(tmlPage.getInstanceName());
+ }
+
+ SdkUtils.createVm(page.getVmPath(), tmlPage.getInstanceName(), page.getVmTarget(), page
+
+ .getAbiType(), page.getVmSkin(), page.getUseSnapshot(), (page.getSDCard().length() == 0
+ ? null : page.getSDCard()));
+
+ }
+ catch (CoreException e)
+ {
+ EclipseUtils.showErrorDialog("Could not create instance ", e.getStatus().getMessage());
+
+ StudioLogger.error(WizardMainPageOperation.class,
+ "Could not create AVD: " + tmlPage.getInstanceName(), e);
+ }
+
+ Collection<String> vmInstances = SdkUtils.getAllValidVmNames();
+ InstancesListRefresh.refreshStatus(vmInstances, tmlPage.getInstanceName());
+
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardStartupOptionsPage.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardStartupOptionsPage.java
new file mode 100644
index 0000000..02bbc97
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/device/ui/wizard/WizardStartupOptionsPage.java
@@ -0,0 +1,195 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.device.ui.wizard;
+
+import java.io.File;
+import java.util.Properties;
+
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.sequoyah.device.framework.ui.wizard.IInstanceProperties;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.core.skin.SkinFramework;
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.device.instance.options.StartupOptionsMgt;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite.PropertyCompositeChangeEvent;
+import com.motorola.studio.android.emulator.device.ui.AbstractPropertiesComposite.PropertyCompositeChangeListener;
+import com.motorola.studio.android.emulator.device.ui.StartupOptionsComposite;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class represents the second wizard page for Android Emulator Device Instance creation.
+ * <br>
+ * It shows all startup options information and validates it, setting an appropriate error message when
+ * applicable.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * - Allow user to enter startup options information for creating a new Android Emulator Device Instance
+ * <br>
+ * - Validates tartup options information entered by user
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * WizardPage: extends this class
+ * <br>
+ * StartupOptionsMainComposite: uses this composite as the main widget
+ * <br>
+ * USAGE:
+ * <br>
+ * This wizard page must be added as the second page on the class implementing the New Android
+ * Emulator Device Instance Wizard.
+ */
+public class WizardStartupOptionsPage extends WizardPage implements IInstanceProperties
+{
+ private StartupOptionsComposite startupOptionsComposite;
+
+ private IAndroidSkin skin;
+
+ // handle changes
+ private final PropertyCompositeChangeListener compositeChangeListener =
+ new PropertyCompositeChangeListener()
+ {
+ public void compositeChanged(PropertyCompositeChangeEvent e)
+ {
+ String errorMessage = startupOptionsComposite.getErrorMessage();
+ if (errorMessage != null)
+ {
+ setErrorMessage(errorMessage);
+ setPageComplete(false);
+ }
+ else
+ {
+ setErrorMessage(null);
+ setPageComplete(true);
+ }
+ }
+ };
+
+ /**
+ * Creates a WizardMainPage object.
+ */
+ public WizardStartupOptionsPage()
+ {
+ super(EmulatorNLS.UI_WizardStartupOptionsPage_PageMessage);
+
+ }
+
+ /**
+ * Creates the UI for this wizard page.
+ * It uses the PropertiesMainComposite only.
+ */
+ public void createControl(Composite parent)
+ {
+ // Get selected Skin name
+ WizardMainPage page = (WizardMainPage) this.getPreviousPage();
+ boolean canCalculateScale = true;
+ try
+ {
+ if (page.getSkinId() != null)
+ {
+ SkinFramework sm = new SkinFramework();
+ skin =
+ sm.getSkinById(page.getSkinId(), new File(page.getVmTarget().getLocation()
+ + "skins" + File.separator + page.getVmSkin()));
+ }
+ }
+ catch (SkinException e)
+ {
+ StudioLogger.error(this.getClass(),
+ "Error reading instance skin during startup options page creation", e);
+ canCalculateScale = false;
+ }
+
+ setTitle(EmulatorNLS.UI_General_WizardTitle);
+ setErrorMessage(null);
+ if (getMessage() != null)
+ {
+ setMessage(EmulatorNLS.UI_WizardStartupOptionsPage_PageMessage);
+ }
+
+ // Define layout
+ GridLayout mainLayout = new GridLayout(1, false);
+ mainLayout.marginTop = 0;
+ mainLayout.marginWidth = 0;
+ mainLayout.marginHeight = 0;
+
+ // Create Startup Options area
+ startupOptionsComposite =
+ new StartupOptionsComposite(parent, NativeUIUtils.getDefaultCommandLine(), skin,
+ canCalculateScale);
+
+ AbstractPropertiesComposite.addCompositeChangeListener(compositeChangeListener);
+
+ // Set layout
+ startupOptionsComposite.setLayout(mainLayout);
+
+ setControl(startupOptionsComposite);
+
+ setPageComplete(true);
+ }
+
+ @Override
+ public boolean isPageComplete()
+ {
+ boolean isComplete = true;
+ if (startupOptionsComposite != null)
+ {
+ isComplete = (startupOptionsComposite.getErrorMessage() == null);
+ }
+ return isComplete;
+ }
+
+ @Override
+ public void dispose()
+ {
+ AbstractPropertiesComposite.removeCompositeChangeListener(compositeChangeListener);
+ setControl(null);
+ if (startupOptionsComposite != null)
+ {
+ startupOptionsComposite.dispose();
+ startupOptionsComposite = null;
+ }
+ super.dispose();
+ }
+
+ public Properties getProperties()
+ {
+ Properties properties = new Properties();
+
+ if (startupOptionsComposite == null)
+ {
+ properties.setProperty(IDevicePropertiesConstants.commandline,
+ NativeUIUtils.getDefaultCommandLine());
+
+ }
+ else
+ {
+ properties.setProperty(IDevicePropertiesConstants.commandline,
+ StartupOptionsMgt.getParamList());
+ }
+
+ return properties;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/i18n/EmulatorNLS.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/i18n/EmulatorNLS.java
new file mode 100644
index 0000000..dc95c9c
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/i18n/EmulatorNLS.java
@@ -0,0 +1,428 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.i18n;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * DESCRIPTION:
+ * This class is the NLS component for Emulator Core plugin.
+ * It is the main class for internationalization
+ *
+ * RESPONSIBILITY:
+ * Provide local strings for using throughout the tool
+ *
+ * COLABORATORS:
+ * coremessages.properties: file that contains the strings that will be provided
+ * to the plugin
+ *
+ * USAGE:
+ * Use any of the public static variables for accessing the local strings
+ */
+public class EmulatorNLS extends NLS
+{
+
+ /**
+ * The bundle location.
+ * It refers to messages.properties file inside this package
+ */
+
+ static
+ {
+ NLS.initializeMessages("com.motorola.studio.android.emulator.i18n.emulatorNLS",
+ EmulatorNLS.class);
+ }
+
+ /*
+ * Generic string area
+ */
+
+ public static String GEN_Error;
+
+ public static String GEN_Warning;
+
+ public static String GEN_Question;
+
+ /*
+ * Exception string area
+ */
+
+ public static String EXC_SkinFramework_CreateIAndroidSkin;
+
+ /*
+ * Warning string area
+ */
+
+ public static String WARN_SkinFramework_SkinNotInstalled;
+
+ public static String WARN_SkinFramework_InvalidInstalledSkinsNotLoaded;
+
+ /*
+ * Error string area
+ */
+
+ public static String ERR_SrcDestComposite_InvalidFillingBase;
+
+ public static String ERR_SrcDestComposite_InvalidFillingPhoneNumber;
+
+ public static String ERR_SrcDestComposite_InvalidFillingEmulator;
+
+ /*
+ * Information string area
+ */
+
+ /*
+ * Question string area
+ */
+
+ /*
+ * UI string area
+ */
+
+ public static String UI_SrcDestComposite_OriginatingRunningEmulatorLabel;
+
+ public static String UI_SrcDestComposite_DestinationRunningEmulatorLabel;
+
+ public static String UI_SrcDestComposite_OriginatingPhoneNumberLabel;
+
+ public static String UI_SrcDestComposite_DestinationPhoneNumberLabel;
+
+ /*
+ * Exception string area
+ */
+
+ public static String EXC_AndroidEmulatorStarter_TimeoutWhileRunningProtocol;
+
+ public static String EXC_AndroidEmulatorStarter_EmulatorStartCanceled;
+
+ public static String EXC_AndroidEmulatorReseter_ErrorWhilePerformingDeleteOperation;
+
+ public static String EXC_AndroidEmulatorReseter_ErrorWhilePerformingSnapshotCopyOperation;
+
+ public static String EXC_AndroidEmulatorReseter_ErrorWhilePerformingDeleteSnapshotOperation;
+
+ public static String EXC_AndroidEmulatorStarter_ProcessTerminated;
+
+ public static String EXC_TimeoutWhileStarting;
+
+ public static String EXC_VncServerNotRunning;
+
+ public static String EXC_CouldNotStartProtocol;
+
+ public static String EXC_AndroidExceptionHandler_CannotRunStopService;
+
+ public static String EXC_AndroidLogicUtils_CannotStartProcess;
+
+ public static String EXC_AndroidLogicUtils_DeviceIsOffline;
+
+ /*
+ * Error string area
+ */
+
+ public static String ERR_AndroidEmulatorStarter_InstanceNullPointer;
+
+ public static String ERR_AndroidEmulatorStarter_NoLogicAvailableForStart;
+
+ public static String ERR_AndroidLogicPlugin_EmulatorStopped;
+
+ public static String ERR_AndroidLogicPlugin_InvalidTimeoutValue;
+
+ public static String ERR_TransferFilesLogic_NotEnoughInformation;
+
+ /*
+ * Question string area
+ */
+
+ public static String QUESTION_AndroidEmulatorReseter_ConfirmationText;
+
+ public static String QUESTION_AndroidEmulatorStopper_StopEmulatorQuestion;
+
+ public static String QUESTION_AndroidExceptionHandler_ImpossibleToReconnect;
+
+ public static String QUESTION_AndroidEmulatorReseter_Yes;
+
+ public static String QUESTION_AndroidEmulatorReseter_No;
+
+ /*
+ * Information string area
+ */
+
+ public static String INFO_ConnectVncLogic_UserCancelledVncServerStart;
+
+ /*
+ * Progress monitor string area
+ */
+
+ public static String MON_AndroidEmulatorStarter_ConnectingToEmulator;
+
+ public static String MON_AndroidEmulatorStopper_DisposingInstance;
+
+ public static String MON_AndroidEmulatorStopper_StopVm;
+
+ public static String MON_AndroidEmulatorStarter_Canceling;
+
+ public static String DPISCALECALCULATOR_Error_MonitorDpi;
+
+ public static String DPISCALECALCULATOR_Error_MonitorSize;
+
+ public static String DPISCALECALCULATOR_Error_ScreenSize;
+
+ public static String DPISCALECALCULATOR_MonitorDpi_Label;
+
+ public static String DPISCALECALCULATOR_MonitorDpiSize_Label;
+
+ public static String DPISCALECALCULATOR_MonitorDpivalue_Label;
+
+ public static String DPISCALECALCULATOR_Regex_TwoDigits;
+
+ public static String DPISCALECALCULATOR_ResultGroup_Title;
+
+ public static String DPISCALECALCULATOR_ResultMonitorDpi_Label;
+
+ public static String DPISCALECALCULATOR_ResultScale_Label;
+
+ public static String DPISCALECALCULATOR_ScreenSize_Label;
+
+ public static String DPISCALECALCULATOR_Title;
+
+ /*
+ * Error string area
+ */
+ public static String ERR_PropertiesMainComposite_MissingTimeoutValue;
+
+ public static String ERR_PropertiesMainComposite_TimeoutValueIsNotPositiveInteger;
+
+ public static String ERR_PropertiesMainComposite_MissingSDCardPath;
+
+ public static String ERR_PropertiesMainComposite_MissingSDCardSize;
+
+ public static String ERR_PropertiesMainComposite_SDCardPathIsNotValid;
+
+ public static String ERR_PropertiesMainComposite_SDCardSizeIsNotPositiveInteger;
+
+ public static String ERR_PropertiesMainComposite_ABINotAvailable;
+
+ // Startup options - all
+ public static String ERR_PropertiesMainComposite_StartupOpt_NoQuotes;
+
+ // Startup options - text
+ public static String ERR_PropertiesMainComposite_StartupOpt_TextBlank;
+
+ // Startup options - number
+ public static String ERR_PropertiesMainComposite_StartupOpt_NumberRequired;
+
+ public static String ERR_PropertiesMainComposite_StartupOpt_NumberMustBeInteger;
+
+ public static String ERR_PropertiesMainComposite_StartupOpt_NumberMustBePositiveInteger;
+
+ public static String ERR_PropertiesMainComposite_StartupOpt_NumberIntRange;
+
+ // Startup options - path
+ public static String ERR_PropertiesMainComposite_StartupOpt_PathRequired;
+
+ public static String ERR_PropertiesMainComposite_StartupOpt_PathDirNotExist;
+
+ public static String ERR_PropertiesMainComposite_StartupOpt_PathMustBeDir;
+
+ public static String ERR_PropertiesMainComposite_StartupOpt_PathFileNotExist;
+
+ public static String ERR_PropertiesMainComposite_StartupOpt_PathMustBeFile;
+
+ public static String ERR_PropertiesMainComposite_StartupOpt_PathIncorrectFileType;
+
+ /*
+ * Info string area
+ */
+ public static String INFO_InfoComposite_EmulatorDefinitionNotFound;
+
+ /*
+ * UI string area
+ */
+ public static String UI_General_BrowseButtonLabel;
+
+ public static String UI_General_WizardTitle;
+
+ public static String UI_PropertiesMainComposite_NameLabel;
+
+ public static String UI_PropertiesMainComposite_EmulatorWindowMode_GroupTitle;
+
+ public static String UI_PropertiesMainComposite_EmulatorWindowMode_NativeLabel;
+
+ public static String UI_PropertiesMainComposite_EmulatorWindowMode_VncLabel;
+
+ public static String UI_PropertiesMainComposite_TimeoutLabel;
+
+ public static String UI_WizardMainPage_PageName;
+
+ public static String UI_WizardStartupOptionsPage_PageMessage;
+
+ public static String UI_AndroidDeviceInstance_StopInstanceJob;
+
+ public static String UI_DpiScale_Calculator;
+
+ /*
+ * Wizard - VM area
+ */
+ public static String UI_PropertiesMainComposite_TargetLabel;
+
+ public static String UI_PropertiesMainComposite_SkinLabel;
+
+ public static String UI_PropertiesMainComposite_PathLabel;
+
+ public static String UI_PropertiesMainComposite_SDCardLabel;
+
+ public static String UI_PropertiesMainComposite_SDCardNoneLabel;
+
+ public static String UI_PropertiesMainComposite_SDCardExistingLabel;
+
+ public static String UI_PropertiesMainComposite_SDCardNewLabel;
+
+ public static String UI_PropertiesMainComposite_SDCardPathLabel;
+
+ public static String UI_PropertiesMainComposite_SDCardSizeLabel;
+
+ public static String UI_PropertiesMainComposite_PathGroupTitle;
+
+ public static String UI_PropertiesMainComposite_UseDefaultPath;
+
+ /*
+ * Wizard - VM area errors
+ */
+ public static String ERR_PropertiesMainComposite_VmTargetEmpty;
+
+ public static String ERR_PropertiesMainComposite_VmSkinEmpty;
+
+ public static String ERR_PropertiesMainComposite_VmPathInvalid;
+
+ /*
+ * Question string area
+ */
+ public static String WizardMainPage_NO_SDK_CONFIGURED_MSG;
+
+ public static String UI_SdkSetup_CreateAVD_Title;
+
+ public static String UI_SdkSetup_CreateAVD_Message;
+
+ /*
+ * Exception string area
+ */
+
+ public static String EXC_General_CannotRunStopService;
+
+ public static String EXC_AncroidView_CannotRunMultipleStopServices;
+
+ public static String EXC_AndroidView_ErrorStartingScreens;
+
+ public static String EXC_AbstractZoomHandler_InstanceNotFound;
+
+ public static String EXC_AndroidView_ViewNotFound;
+
+ /*
+ * Warning string area
+ */
+
+ /*
+ * Error string area
+ */
+
+ public static String ERR_AndroidView_ProtocolImplementerNotSupported;
+
+ public static String EXC_AbstractAndroidView_ViewNotAccessibleProgramatically;
+
+ /*
+ * Information string area
+ */
+
+ /*
+ * Question string area
+ */
+
+ public static String QUESTION_AndroidView_StopAllInstancesOnDisposeTitle;
+
+ public static String QUESTION_AndroidView_StopAllInstancesOnDisposeMessage;
+
+ public static String QUESTION_AbstractAndroidView_OpenViewForStartedEmulatorsTitle;
+
+ public static String QUESTION_AbstractAndroidView_OpenViewForStartedEmulatorsMessage;
+
+ public static String QUESTION_RunningInstancesOnClose_Title;
+
+ public static String QUESTION_RunningInstancesOnClose_Text;
+
+ public static String QUESTION_NativeWindow_LooseOriginalScale_Title;
+
+ public static String QUESTION_NativeWindow_LooseOriginalScale_Text;
+
+ /*
+ * Warn string area
+ */
+ public static String WARN_RunningInstancesOnClose_Linux_Title;
+
+ public static String WARN_RunningInstancesOnClose_Linux_Text;
+
+ /*
+ * UI string area
+ */
+
+ public static String UI_AbstractAndroidView_StopInstanceJob;
+
+ public static String UI_LayoutContributionItem_NoLayoutsAvailable;
+
+ /*
+ * Progress monitor string area
+ */
+ public static String ERR_CannotConnectToVNC;
+
+ public static String ERR_StopEmulatorHandler_NotAnAndroidEmulator;
+
+ public static String ERR_StartEmulatorHandler_NotAnAndroidEmulator;
+
+ public static String ERR_AndroidSkinTranslator_ErrorReadingKeycodeFile;
+
+ public static String ERR_AndroidSkin_NoLayoutLoaded;
+
+ public static String ERR_AndroidSkin_ProvidedSkinPathIsNotADirectory;
+
+ public static String ERR_AndroidSkin_InvalidLayoutProvided;
+
+ public static String ERR_LayoutFileParser_BracketsDoNotMatch;
+
+ public static String ERR_LayoutFileParser_LayoutFileCouldNotBeRead;
+
+ public static String PropertiesMainComposite_ABITypeLabel;
+
+ public static String PropertiesMainComposite_ProxySettings_CheckboxLabel;
+
+ public static String PropertiesMainComposite_ProxySettings_GroupTitle;
+
+ public static String PropertiesMainComposite_ProxySettings_LinkToPreference;
+
+ public static String PropertiesMainComposite_SaveSnapshot;
+
+ public static String PropertiesMainComposite_SDCard_Size_Invalid_Integer;
+
+ public static String PropertiesMainComposite_SnapshotSettings;
+
+ public static String PropertiesMainComposite_startFromSnapshot;
+
+ public static String PropertiesMainComposite_UseSnapshot;
+
+ public static String RepairAvdHandler_AVD_NOT_REPAIRABLE;
+
+ public static String RepairAvdHandler_Not_Android_Instance;
+
+ public static String StartupOptionsComposite_Error_Loading_Skin_Cant_Calculate_Scale;
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/i18n/emulatorNLS.properties b/src/plugins/emulator/src/com/motorola/studio/android/emulator/i18n/emulatorNLS.properties
new file mode 100644
index 0000000..44527f9
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/i18n/emulatorNLS.properties
@@ -0,0 +1,173 @@
+#
+# Copyright (C) 2012 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.
+#
+
+GEN_Error=Error
+GEN_Warning=Warning
+GEN_Question=Question
+EXC_SkinFramework_CreateIAndroidSkin=There was an internal error while loading the {0} skin.\nAn application restart may solve this problem, but if it recurs, you may need to reinstall MOTODEV Studio.
+WARN_SkinFramework_SkinNotInstalled=The skin named {0} is not installed.\nAlthough this does not prevent the Android emulator from working, this skin is not accessible.\nIf this error recurs, you may need to reinstall MOTODEV Studio.
+WARN_SkinFramework_InvalidInstalledSkinsNotLoaded=Skin {0} is invalid and was not loaded.\nAlthough this does not prevent the Android emulator from working, this skin is not accessible.\nIf this error recurs, you may need to reinstall MOTODEV Studio.
+ERR_SrcDestComposite_InvalidFillingBase=Errors were found. Check the following:\n{0}{1}
+ERR_SrcDestComposite_InvalidFillingPhoneNumber=\n\t- In the phone number field, be sure you have entered only digits, with no additional symbols such as hyphens, parentheses, or spaces.
+ERR_SrcDestComposite_InvalidFillingEmulator=\n\t- In the emulator list, be sure you have selected a running emulator.
+UI_SrcDestComposite_OriginatingRunningEmulatorLabel=Originating Android Emulator :
+UI_SrcDestComposite_DestinationRunningEmulatorLabel=Destination Android Emulator :
+UI_SrcDestComposite_OriginatingPhoneNumberLabel=Originating Phone # :
+UI_SrcDestComposite_DestinationPhoneNumberLabel=Destination Phone # :
+EXC_AndroidEmulatorStarter_TimeoutWhileRunningProtocol=An error is preventing the Android Emulator from being displayed. \
+ Some possible causes are:\n\t- The timeout interval is too short. You may need to increase the timeout value on \
+ the Android Virtual Device property page.\n\nSince the emulator is not fully functional, MOTODEV Studio is \
+ aborting the emulator start process.
+EXC_AndroidEmulatorStarter_EmulatorStartCanceled=Android Emulator start process canceled by user.
+EXC_AndroidEmulatorReseter_ErrorWhilePerformingDeleteOperation=Reset operation could not be completed. Manually remove all \
+ files, except config.ini and snapshots.img (if it exists), from {0} to complete the process. If you don't have the needed permissions, contact your system administrator.
+EXC_AndroidEmulatorReseter_ErrorWhilePerformingDeleteSnapshotOperation=Reset operation could not be completed. Manually remove all \
+ files, except config.ini from {0} to complete the process. If the snapshots.img file exists, replace it by {1}. If you don't have the needed permissions, contact your system administrator.
+EXC_AndroidEmulatorReseter_ErrorWhilePerformingSnapshotCopyOperation=Error while resetting the snapshot file. Manually copy the file from {0} \
+ to {1} to complete the process. If you don't have the needed permissions, contact your system administrator.
+EXC_AndroidEmulatorStarter_ProcessTerminated=The emulator process terminated unexpectedly:
+EXC_TimeoutWhileStarting=The timeout was reached while trying to start Android Emulator {0}.
+EXC_VncServerNotRunning=The VNC server is not running on {0}.\n{1}
+EXC_CouldNotStartProtocol=Couldn't establish a connection to the Android emulator.
+EXC_AndroidExceptionHandler_CannotRunStopService=The device instance could not be stopped.
+EXC_AndroidLogicUtils_CannotStartProcess=It was not possible to start the emulator process. The instance start process will be aborted.
+EXC_AndroidLogicUtils_DeviceIsOffline=The device is not online.
+ERR_AndroidEmulatorStarter_InstanceNullPointer=Android emulator was not defined. The operation will not be performed.
+ERR_AndroidEmulatorStarter_NoLogicAvailableForStart=An error is preventing the Android emulator from being started. \
+ Try reinstalling or updating MOTODEV Studio.
+ERR_AndroidLogicPlugin_EmulatorStopped=The Android emulator process has unexpectedly stopped running. The instance {0} is now stopped.
+ERR_AndroidLogicPlugin_InvalidTimeoutValue=Invalid timeout value: {0}
+ERR_TransferFilesLogic_NotEnoughInformation=There is not enough information about the files being transferred to the emulator.
+INFO_ConnectVncLogic_UserCancelledVncServerStart=User canceled the execution of the VNC server!
+QUESTION_AndroidEmulatorReseter_ConfirmationText=WARNING: Resetting the emulator(s) will delete all user-specific data, such as installed applications, custom files (such as user databases) and any customizations performed on the device(s).\n\nDo you want to proceed?
+QUESTION_AndroidEmulatorStopper_StopEmulatorQuestion=Are you sure you want to stop "{0}"?
+QUESTION_AndroidExceptionHandler_ImpossibleToReconnect=Cannot reconnect to the emulator. Do you want to retry?
+MON_AndroidEmulatorStarter_ConnectingToEmulator=Connecting to Android emulator instance...
+MON_AndroidEmulatorStopper_DisposingInstance=Disposing of the Android emulator instance
+MON_AndroidEmulatorStopper_StopVm=Stopping the device instance. This may take a few minutes...
+MON_AndroidEmulatorStarter_Canceling=Canceling
+QUESTION_AndroidEmulatorReseter_Yes=Yes
+QUESTION_AndroidEmulatorReseter_No=No
+DPISCALECALCULATOR_Error_MonitorDpi=Monitor Dpi value should contain only digits
+DPISCALECALCULATOR_Error_MonitorSize=Monitor Size should contain only digits and dots
+DPISCALECALCULATOR_Error_ScreenSize=Screen size must contain only digits and dots
+DPISCALECALCULATOR_MonitorDpi_Label=Monitor Screen Resolution
+DPISCALECALCULATOR_MonitorDpiSize_Label=Based on Monitor Size (in)
+DPISCALECALCULATOR_MonitorDpivalue_Label=Value (dpi)
+DPISCALECALCULATOR_Regex_TwoDigits=\\d+(\\.\\d{1,2})*
+DPISCALECALCULATOR_ResultGroup_Title=Result
+DPISCALECALCULATOR_ResultMonitorDpi_Label=Resolution (dpi):
+DPISCALECALCULATOR_ResultScale_Label=Scale:
+DPISCALECALCULATOR_ScreenSize_Label=Device Screen Size(in) :
+DPISCALECALCULATOR_Title=Screen Resolution / Scale Calculator
+ERR_PropertiesMainComposite_MissingTimeoutValue=You must provide a timeout value
+ERR_PropertiesMainComposite_TimeoutValueIsNotPositiveInteger=The timeout value must be a positive integer
+ERR_PropertiesMainComposite_MissingSDCardPath=You must provide a valid SD Card path
+ERR_PropertiesMainComposite_MissingSDCardSize=You must provide the SD Card size
+ERR_PropertiesMainComposite_SDCardPathIsNotValid=The SD Card path is not valid
+ERR_PropertiesMainComposite_SDCardSizeIsNotPositiveInteger=The SD Card size must be greater than 9MB
+INFO_InfoComposite_EmulatorDefinitionNotFound=The selected emulator type was not found.\
+ The device was not created.\
+ The default type has been selected instead.
+UI_General_BrowseButtonLabel=Browse...
+UI_General_WizardTitle=New Android Virtual Device Instance
+UI_PropertiesMainComposite_NameLabel=Name :
+UI_PropertiesMainComposite_TimeoutLabel=Timeout (sec):
+UI_PropertiesMainComposite_EmulatorWindowMode_GroupTitle=Internal Emulator Window
+UI_PropertiesMainComposite_EmulatorWindowMode_NativeLabel=Show the native Emulator window within an Eclipse view (recommended)
+UI_PropertiesMainComposite_EmulatorWindowMode_VncLabel=Use VNC to show the Emulator within an Eclipse view
+UI_WizardMainPage_PageName=Enter Android Virtual Device Instance Main Information
+UI_WizardStartupOptionsPage_PageMessage=Enter the startup options you want the Android Virtual Device instance to use.
+UI_AndroidDeviceInstance_StopInstanceJob=Stop Device Instance
+UI_DpiScale_Calculator=Calculate
+UI_PropertiesMainComposite_TargetLabel=AVD Target :
+UI_PropertiesMainComposite_SkinLabel=AVD Skin :
+UI_PropertiesMainComposite_PathGroupTitle=AVD Path
+UI_PropertiesMainComposite_PathLabel=AVD Path :
+UI_PropertiesMainComposite_SDCardLabel=SD Card
+UI_PropertiesMainComposite_SDCardNoneLabel=None
+UI_PropertiesMainComposite_SDCardExistingLabel=Existing
+UI_PropertiesMainComposite_SDCardNewLabel=New
+UI_PropertiesMainComposite_SDCardPathLabel=SD Card Path :
+UI_PropertiesMainComposite_SDCardSizeLabel=SD Card Size :
+UI_PropertiesMainComposite_UseDefaultPath= Use default
+ERR_PropertiesMainComposite_VmTargetEmpty=You must provide a target for the AVD
+ERR_PropertiesMainComposite_VmSkinEmpty=You must provide a skin for the AVD
+ERR_PropertiesMainComposite_VmPathInvalid=You must provide a valid and existing path for the AVD
+ERR_PropertiesMainComposite_StartupOpt_NoQuotes=The value for {0} contains quotes, which are not allowed.
+ERR_PropertiesMainComposite_StartupOpt_TextBlank=Provide a value for {0}.
+ERR_PropertiesMainComposite_StartupOpt_NumberRequired=Provide a value for {0}.
+ERR_PropertiesMainComposite_StartupOpt_NumberMustBeInteger=The value for {0} must be an integer.
+ERR_PropertiesMainComposite_StartupOpt_NumberMustBePositiveInteger=The value for {0} must be a positive integer.
+ERR_PropertiesMainComposite_StartupOpt_NumberIntRange=The value for {0} must be a value between {1} and {2}.
+ERR_PropertiesMainComposite_StartupOpt_PathRequired=Provide a path for {0}.
+ERR_PropertiesMainComposite_StartupOpt_PathDirNotExist=The directory specified for {0} doesn't exist. Specify a valid directory.
+ERR_PropertiesMainComposite_StartupOpt_PathMustBeDir=Select a folder for {0}, not a file.
+ERR_PropertiesMainComposite_StartupOpt_PathFileNotExist=The file specified for {0} doesn't exist. Specify a valid path.
+ERR_PropertiesMainComposite_StartupOpt_PathMustBeFile=Select a file for {0}, not a folder.
+ERR_PropertiesMainComposite_StartupOpt_PathIncorrectFileType=The file specified for {0} is not a {1} file. Specify a valid path.
+WizardMainPage_NO_SDK_CONFIGURED_MSG=Configure an SDK before creating an AVD.
+
+UI_SdkSetup_CreateAVD_Title = Create AVD
+UI_SdkSetup_CreateAVD_Message = A valid AVD (Android Virtual Device) was not detected. Do you want to create one?
+EXC_General_CannotRunStopService=The instance could not be stopped automatically.
+EXC_AncroidView_CannotRunMultipleStopServices=One or more instances could not be stopped automatically.
+EXC_AndroidView_ErrorStartingScreens=There was an error while trying to refresh the emulator screen. \
+ Restart the emulator to refresh the screen.
+EXC_AbstractZoomHandler_InstanceNotFound=The currently displayed Android emulator instance is no longer \
+ available.\nThe view is being closed to reflect this.
+EXC_AndroidView_ViewNotFound=The Emulator view could not be accessed.\nThe requested operation will not \
+ be performed.
+ERR_AndroidView_ProtocolImplementerNotSupported=The device instance is not using a communication protocol \
+ supported by this viewer. The instance will be stopped.
+QUESTION_AndroidView_StopAllInstancesOnDisposeTitle=Close Android Emulator View
+QUESTION_AndroidView_StopAllInstancesOnDisposeMessage=Do you wish to stop the running Android emulator \
+ instances as well?
+UI_AbstractAndroidView_StopInstanceJob=Stop Device
+QUESTION_AbstractAndroidView_OpenViewForStartedEmulatorsTitle=Open Android Emulator View
+QUESTION_AbstractAndroidView_OpenViewForStartedEmulatorsMessage=Do you wish to display the Android Virtual Device(s) within an Eclipse view?
+QUESTION_RunningInstancesOnClose_Title = Running Android Virtual Devices
+QUESTION_RunningInstancesOnClose_Text = There are Android Virtual Devices running. Do you want to stop them?
+QUESTION_NativeWindow_LooseOriginalScale_Title = Start up arguments
+QUESTION_NativeWindow_LooseOriginalScale_Text = By zooming the emulator image you will lose the scale options used to start the Android Virtual Device. Do you want to continue?
+WARN_RunningInstancesOnClose_Linux_Title = Running Android Virtual Devices
+WARN_RunningInstancesOnClose_Linux_Text = The running Android Virtual Devices will be stopped
+UI_LayoutContributionItem_NoLayoutsAvailable=No layouts
+ERR_CannotConnectToVNC=Could not establish a connection to emulator "{0}"
+EXC_AbstractAndroidView_ViewNotAccessibleProgramatically=The Android Emulator view could not be opened automatically. \
+ Open it manually by selecting "Show View" from the "Window" menu.
+ERR_StopEmulatorHandler_NotAnAndroidEmulator=The service is being run with a device instance that is not supported by the service. Aborting execution.
+ERR_StartEmulatorHandler_NotAnAndroidEmulator=The service is being run with a device instance that is not supported by the service. Aborting execution.
+ERR_AndroidSkinTranslator_ErrorReadingKeycodeFile=It was not possible to retrieve key-related data from the skin. It will not be possible to use the mouse in this session.
+ERR_AndroidSkin_NoLayoutLoaded=No skin was loaded. Check if the skin assigned to the current AVD is available and not corrupted.
+ERR_AndroidSkin_InvalidLayoutProvided=The provided layout name does not exist in the skin being used by the instance. The current operation is being aborted.
+ERR_AndroidSkin_ProvidedSkinPathIsNotADirectory=The selected skin was not found. Check your AVD configuration and try again later.
+ERR_LayoutFileParser_BracketsDoNotMatch=The skin layout file is corrupted. It is not possible to load this skin. It will not be possible to use the mouse in this session.
+ERR_LayoutFileParser_LayoutFileCouldNotBeRead=The skin layout file could not be read. It is not possible to load this skin. It will not be possible to use the mouse in this session.
+PropertiesMainComposite_ABITypeLabel=ABI Type:
+PropertiesMainComposite_ProxySettings_CheckboxLabel=Use settings from Eclipse Network Settings
+PropertiesMainComposite_ProxySettings_GroupTitle=Proxy Settings
+PropertiesMainComposite_ProxySettings_LinkToPreference=<a>Configure network settings</a>
+PropertiesMainComposite_SaveSnapshot=Save to snapshot on exit
+PropertiesMainComposite_SDCard_Size_Invalid_Integer=The SD card size is not a valid integer.
+PropertiesMainComposite_SnapshotSettings=Snapshot Settings
+PropertiesMainComposite_startFromSnapshot=Launch emulator from snapshot
+PropertiesMainComposite_UseSnapshot=Enable snapshot
+RepairAvdHandler_AVD_NOT_REPAIRABLE=The selected AVD is not repairable.
+RepairAvdHandler_Not_Android_Instance=Aborting repair service. This is not an Android Emulator instance...
+
+ERR_PropertiesMainComposite_ABINotAvailable=You must select one ABI type to create an AVD. If none are available, use the Android SDK Manager to download the ABI system image.
+StartupOptionsComposite_Error_Loading_Skin_Cant_Calculate_Scale=Could not read emulator skin. Calculating scale will not be possible.
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AbstractStartAndroidEmulatorLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AbstractStartAndroidEmulatorLogic.java
new file mode 100644
index 0000000..004ac7d
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AbstractStartAndroidEmulatorLogic.java
@@ -0,0 +1,69 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.logic;
+
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+public abstract class AbstractStartAndroidEmulatorLogic implements IAndroidLogic
+{
+
+ public static enum LogicMode
+ {
+ START_MODE, TRANSFER_AND_CONNECT_VNC, RESTART_VNC_SERVER, DO_NOTHING;
+ }
+
+ public final void execute(IAndroidLogicInstance instance, int timeout, IProgressMonitor monitor)
+ throws InstanceStartException, StartCancelledException, StartTimeoutException,
+ IOException
+ {
+ this.execute(instance, LogicMode.START_MODE, timeout, monitor);
+ }
+
+ public final void execute(IAndroidLogicInstance instance, LogicMode mode, int timeout,
+ IProgressMonitor monitor) throws InstanceStartException, StartCancelledException,
+ StartTimeoutException, IOException
+
+ {
+ for (IAndroidLogic logic : getLogicCollection(instance, mode))
+ {
+ long timeoutLimit = AndroidLogicUtils.getTimeoutLimit(timeout);
+ AndroidLogicUtils.testCanceled(monitor);
+ AndroidLogicUtils.testTimeout(timeoutLimit, NLS.bind(
+ EmulatorNLS.EXC_TimeoutWhileStarting, instance.getName()));
+ info("Executing " + logic.getClass().getSimpleName() + " for " + instance);
+ long startTime = System.currentTimeMillis();
+ logic.execute(instance, timeout, monitor);
+ long endTime = System.currentTimeMillis();
+ int duration = (int) (endTime - startTime);
+ timeout = timeout - duration;
+ }
+ }
+
+ public abstract Collection<IAndroidLogic> getLogicCollection(IAndroidLogicInstance instance,
+ LogicMode mode);
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AndroidExceptionHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AndroidExceptionHandler.java
new file mode 100644
index 0000000..0bb31bd
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AndroidExceptionHandler.java
@@ -0,0 +1,392 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.logic;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.IJobManager;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+import org.eclipse.sequoyah.vnc.protocol.PluginProtocolActionDelegate;
+import org.eclipse.sequoyah.vnc.protocol.lib.IProtocolExceptionHandler;
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+import org.eclipse.sequoyah.vnc.protocol.lib.exceptions.InvalidDefinitionException;
+import org.eclipse.sequoyah.vnc.protocol.lib.exceptions.InvalidInputStreamDataException;
+import org.eclipse.sequoyah.vnc.protocol.lib.exceptions.InvalidMessageException;
+import org.eclipse.sequoyah.vnc.protocol.lib.exceptions.MessageHandleException;
+import org.eclipse.sequoyah.vnc.protocol.lib.exceptions.ProtocolHandshakeException;
+import org.eclipse.sequoyah.vnc.protocol.lib.exceptions.ProtocolRawHandlingException;
+
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.core.exception.InstanceNotFoundException;
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.utils.EmulatorCoreUtils;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic.LogicMode;
+
+/**
+ * DESCRIPTION:
+ * Class that defines how to handle internal protocol exceptions
+ *
+ * RESPONSABILITY:
+ * Handle internal protocol exceptions
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class shall be used by Eclipse only
+ */
+public class AndroidExceptionHandler implements IProtocolExceptionHandler
+{
+ private int handlingLevel = 1;
+
+ private boolean checkThreadRunning = false;
+
+ private Lock lock = new ReentrantReadWriteLock().writeLock();
+
+ private static Collection<String> stoppedWithFailure = new HashSet<String>();
+
+ static
+ {
+
+ IJobManager manager = Job.getJobManager();
+ manager.addJobChangeListener(new JobChangeAdapter()
+ {
+ @Override
+ public void done(IJobChangeEvent event)
+ {
+ Job job = event.getJob();
+ if (job.belongsTo(StartVncServerLogic.VNC_SERVER_JOB_FAMILY))
+ {
+ IStatus result = event.getResult();
+ if (!result.isOK() && !(result.getSeverity() == IStatus.CANCEL))
+ {
+ stoppedWithFailure.add(job.getName());
+ }
+ }
+ }
+
+ @Override
+ public void scheduled(IJobChangeEvent event)
+ {
+ Job job = event.getJob();
+ if (job.belongsTo(StartVncServerLogic.VNC_SERVER_JOB_FAMILY))
+ {
+ stoppedWithFailure.remove(job.getName());
+ }
+ }
+ });
+ }
+
+ /**
+ * Handles internal IOExceptions caught by the protocol plugin during its execution.
+ *
+ * @see IProtocolExceptionHandler#handleIOException(ProtocolHandle, IOException)
+ */
+ public void handleIOException(ProtocolHandle handle, IOException e)
+ {
+ error("A socket was broken while communicating to server. Cause: " + e.getMessage());
+ handleException(handle);
+ }
+
+ /**
+ * Handles exceptions thrown by the protocol plugin when it detects an invalid message
+ * definition provided by the plugin which is extending it (in this case, the core plugin).
+ *
+ * @see IProtocolExceptionHandler#handleInvalidDefinitionException(ProtocolHandle, InvalidDefinitionException)
+ */
+ public void handleInvalidDefinitionException(ProtocolHandle handle, InvalidDefinitionException e)
+ {
+ // This exception should not happen, because the message definitions are provided
+ // by the development team.
+ warn("An invalid message definition was detected. Cause: " + e.getMessage());
+ handleException(handle);
+ }
+
+ /**
+ * Handles exceptions thrown by the protocol plugin when it detects that the data retrieved from
+ * the connection does not match the format defined in the message definition.
+ *
+ * @see IProtocolExceptionHandler#handleInvalidInputStreamDataException(ProtocolHandle, InvalidInputStreamDataException)
+ */
+ public void handleInvalidInputStreamDataException(ProtocolHandle handle,
+ InvalidInputStreamDataException e)
+ {
+ // If the data retrieved from the connection is not as expected (considering
+ // the message definition provided), there is a high chance of errors to happen.
+ // It is likely that the data from stream is no longer synchronized, so the
+ // exception handling for this case is to restart connection.
+
+ error("Some received data is not compatible with the expected definition. Restarting the protocol for synchronization.");
+ handleException(handle);
+ }
+
+ /**
+ * Handles exceptions thrown by the protocol plugin when it detects that the message
+ * provided for sending does not have enough or valid information, given a corresponding
+ * message definition
+ *
+ * @see IProtocolExceptionHandler#handleInvalidMessageException(ProtocolHandle, InvalidMessageException)
+ */
+ public void handleInvalidMessageException(ProtocolHandle handle, InvalidMessageException e)
+ {
+ // This exception should not happen, because the message object data is provided
+ // by the development team. Log only.
+ warn("A message was not constructed according to its definition. Cause: " + e.getMessage());
+ handleException(handle);
+ }
+
+ /**
+ * Handles exceptions thrown by any message handler when they discovers that it
+ * is not possible to handle the message and it is a fatal error for the protocol.
+ *
+ * @see IProtocolExceptionHandler#handleMessageHandleException(ProtocolHandle, MessageHandleException)
+ */
+ public void handleMessageHandleException(ProtocolHandle handle, MessageHandleException e)
+ {
+ // If a message handler throws a MessageHandleException, that means that it cannot
+ // continue. Restart the protocol to guarantee the synchronization.
+ error("A message handler has ended in error and has thrown an exception meaning the protocol cannot continue.");
+ handleException(handle);
+ }
+
+ /**
+ * Handles exceptions thrown by the protocol plugin when it is not possible to
+ * init the protocol, for example because the handshaking procedure has failed.
+ *
+ * @see IProtocolExceptionHandler#handleProtocolHandshakeException(ProtocolHandle, ProtocolHandshakeException)
+ */
+ public void handleProtocolHandshakeException(ProtocolHandle handle, ProtocolHandshakeException e)
+ {
+ error("Could not initialize the protocol.");
+ handleException(handle);
+ }
+
+ /**
+ * Handles exceptions thrown by any raw field handler when they discovers that it
+ * is not possible to handle the field and it is a fatal error for the protocol.
+ *
+ * @see IProtocolExceptionHandler#handleProtocolRawHandlingException(ProtocolHandle, ProtocolRawHandlingException)
+ */
+ public void handleProtocolRawHandlingException(ProtocolHandle handle,
+ ProtocolRawHandlingException e)
+ {
+ // This message should be thrown by raw field handlers when they cannot handle the
+ // raw field and need to abort the protocol execution. Restart the protocol to
+ // guarantee the synchronization.
+ error("A raw field handler has ended in error and has thrown an exception meaning the protocol cannot continue.");
+ handleException(handle);
+ }
+
+ /**
+ * This method will be called whenever an exception happens. It is important to find
+ * out if the failure happened during an start or restart procedure, so that we can
+ * do appropriate handling to each situation.
+ *
+ * @param handle The object that identifies the protocol instance
+ */
+ private void handleException(ProtocolHandle handle)
+ {
+ IAndroidEmulatorInstance instance = null;
+ if (lock.tryLock())
+ {
+ try
+ {
+ instance = EmulatorCoreUtils.getAndroidInstanceByHandle(handle);
+
+ try
+ {
+ debug("Check if device is online: " + instance);
+ if (instance instanceof ISerialNumbered)
+ {
+ String serialNumber = ((ISerialNumbered) instance).getSerialNumber();
+ AndroidLogicUtils.testDeviceStatus(serialNumber);
+ }
+ }
+ catch (Exception e)
+ {
+ error("Device is not online. Abort VNC session...");
+ abort(instance);
+ }
+
+ if ((handlingLevel == 3) && canRestartServer(instance))
+ {
+ handlingLevel--;
+ }
+
+ // Firstly, try to restart only the VNC client. If restarting the VNC client
+ // is not possible, try to restart the VNC server at the device and to connect
+ // to it again. If the start logic cannot be retrieved, delegate the decision
+ // to the user.
+
+ if (handlingLevel == 1)
+ {
+ restartClientOnly(handle);
+ handlingLevel++;
+ }
+ else if (handlingLevel == 2)
+ {
+ restartServerAndClient(instance, handle);
+ handlingLevel++;
+ }
+ else
+ {
+ if (delegateDecisionToUser(handle))
+ {
+ abort(instance);
+ }
+ }
+ }
+ catch (InstanceNotFoundException e)
+ {
+ // If the instance is not found, it means that the instance is stopped.
+ // In this case, a restart is not applicable.
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+ }
+
+ private boolean canRestartServer(IAndroidEmulatorInstance instance)
+ {
+ String name = StartVncServerLogic.VNC_SERVER_JOB_PREFIX + instance.getName();
+ return !stoppedWithFailure.contains(name);
+ }
+
+ private void restartClientOnly(final ProtocolHandle handle)
+ {
+ PluginProtocolActionDelegate.requestRestartProtocol(handle);
+
+ if (!checkThreadRunning)
+ {
+ Runnable r = new Runnable()
+ {
+ public void run()
+ {
+ checkThreadRunning = true;
+ while (checkThreadRunning)
+ {
+ if (PluginProtocolActionDelegate.isProtocolRunning(handle))
+ {
+ handlingLevel = 1;
+ checkThreadRunning = false;
+ }
+
+ try
+ {
+ Thread.sleep(500);
+ }
+ catch (InterruptedException e)
+ {
+ // Do nothing.
+ }
+ }
+ }
+ };
+ (new Thread(r)).start();
+ }
+ }
+
+ private void restartServerAndClient(IAndroidEmulatorInstance instance, ProtocolHandle handle)
+ {
+ try
+ {
+ if (instance instanceof IAndroidLogicInstance)
+ {
+ IAndroidLogicInstance logicInstance = (IAndroidLogicInstance) instance;
+ AbstractStartAndroidEmulatorLogic logic = logicInstance.getStartLogic();
+ logic.execute(logicInstance, LogicMode.TRANSFER_AND_CONNECT_VNC, logicInstance
+ .getTimeout(), new NullProgressMonitor());
+ try
+ {
+ Thread.sleep(1500);
+ }
+ catch (InterruptedException e)
+ {
+ // Do nothing.
+ }
+ PluginProtocolActionDelegate.requestRestartProtocol(handle);
+ }
+ else
+ {
+ handlingLevel = 3;
+ }
+ }
+ catch (Exception e1)
+ {
+ handlingLevel = 3;
+ }
+ }
+
+ /**
+ * In this method, the user is asked whether to retry or not. While the user does not
+ * give up, the instance retries to connect to the emulator. When the user gives up,
+ * the instance is stopped.
+ *
+ * @param handle The object that identifies the protocol instance
+ */
+ private boolean delegateDecisionToUser(ProtocolHandle handle)
+ {
+ boolean abort = true;
+ error("Cannot reconnect to VM. Asking to the user if he/she wants to retry.");
+ if (EclipseUtils.showQuestionDialog(EmulatorNLS.GEN_Question,
+ EmulatorNLS.QUESTION_AndroidExceptionHandler_ImpossibleToReconnect))
+ {
+ info("User chose to retry to reconnect to emulator VNC server.");
+ PluginProtocolActionDelegate.requestRestartProtocol(handle);
+ handlingLevel = 2;
+ abort = false;
+ }
+ return abort;
+ }
+
+ /**
+ *
+ */
+ private void abort(IAndroidEmulatorInstance instance)
+ {
+ info("User chose to stop the instance.");
+ try
+ {
+ checkThreadRunning = false;
+ instance.stop(true);
+ }
+ catch (InstanceStopException e1)
+ {
+ error("Error while running service for stopping virtual machine");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_AndroidExceptionHandler_CannotRunStopService);
+ }
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AndroidLogicUtils.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AndroidLogicUtils.java
new file mode 100644
index 0000000..aad3b07
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/AndroidLogicUtils.java
@@ -0,0 +1,360 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.logic;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * This class is used as an utilities class for operations related to Android Devices
+ *
+ */
+public class AndroidLogicUtils
+{
+ private static final int INITIAL_VNC_PORT_VALUE = 5900;
+
+ public static final String ORIENTATION_BASE_COMMAND = "sendevent /dev/input/event0 ";
+
+ /**
+ * Execute the VM process
+ *
+ * @param cmd the command to be executed
+ * @return the VM process
+ *
+ * @throws AndroidException if the command failed to execute
+ */
+ public static Process executeProcess(final String cmd) throws AndroidException
+ {
+ try
+ {
+ info("Executing command " + cmd);
+ Process vmProcess = Runtime.getRuntime().exec(cmd);
+ return vmProcess;
+ }
+ catch (IOException e)
+ {
+ error("Falied to execute the command: " + cmd);
+ throw new AndroidException(NLS.bind(
+ EmulatorNLS.EXC_AndroidLogicUtils_CannotStartProcess, cmd));
+ }
+ }
+
+ /**
+ * Execute the VM process
+ *
+ * @param cmd the command to be executed, described as an array
+ * @return the VM process
+ *
+ * @throws AndroidException if the command failed to execute
+ */
+ public static Process executeProcess(final String[] cmd) throws AndroidException
+ {
+ String cmdString = "";
+ for (int i = 0; i < cmd.length; i++)
+ {
+ cmdString += cmd[i] + " ";
+
+ }
+
+ return executeProcess(cmd, cmdString);
+ }
+
+ /**
+ * Execute the VM process
+ *
+ * @param cmd The command to be executed, described as an array
+ * @param cmdToLog The command to be logged.
+ *
+ * @return the VM process
+ *
+ * @throws AndroidException if the command failed to execute
+ */
+ public static Process executeProcess(final String[] cmd, final String cmdToLog)
+ throws AndroidException
+ {
+ try
+ {
+ info("Executing command " + cmdToLog);
+ Process vmProcess = Runtime.getRuntime().exec(cmd);
+ return vmProcess;
+ }
+ catch (IOException e)
+ {
+ error("Falied to execute the command: " + cmd);
+ throw new AndroidException(NLS.bind(
+ EmulatorNLS.EXC_AndroidLogicUtils_CannotStartProcess, cmd));
+ }
+ }
+
+ /**
+ }
+
+ /**
+ * Checks if the user has canceled the VM startup
+ *
+ * @param monitor A progress monitor that will give the user feedback about this
+ * long running operation
+ * @param instanceHost The IP address of the started emulator instance
+ *
+ * @return True if the operation can proceed, false otherwise
+ *
+ * @throws StartCancelledException If the user has canceled the start process
+ */
+ public static void testCanceled(IProgressMonitor monitor) throws StartCancelledException
+ {
+ if (monitor.isCanceled())
+ {
+ info("Operation canceled by the user");
+ monitor.subTask(EmulatorNLS.MON_AndroidEmulatorStarter_Canceling);
+
+ throw new StartCancelledException(
+ EmulatorNLS.EXC_AndroidEmulatorStarter_EmulatorStartCanceled);
+ }
+ }
+
+ /**
+ * Checks if the timeout limit has reached
+ *
+ * @param timeoutLimit The system time limit that cannot be overtaken, in milliseconds
+ *
+ * @throws StartTimeoutException When the system time limit is overtaken
+ */
+ public static void testTimeout(long timeoutLimit, String timeoutErrorMessage)
+ throws StartTimeoutException
+ {
+ if (System.currentTimeMillis() > timeoutLimit)
+ {
+ error("The emulator was not up within the set timeout");
+ throw new StartTimeoutException(timeoutErrorMessage);
+ }
+ }
+
+ /**
+ * Get the relative timeout limit, which is the the current time plus the timeout value
+ *
+ * @param timeout timeout value (in milliseconds)
+ * @return Relative timeout limit
+ */
+ public static long getTimeoutLimit(int timeout)
+ {
+ return System.currentTimeMillis() + timeout;
+ }
+
+ /**
+ * Check if the given process is still up and running
+ *
+ * @param p process
+ * @throws InstanceStartException
+ */
+ public static void testProcessStatus(Process p) throws InstanceStartException
+ {
+
+ boolean isRunning;
+ int exitCode;
+
+ try
+ {
+ exitCode = p.exitValue();
+ isRunning = false;
+ }
+ catch (Exception e)
+ {
+ // emulator process is still running... so everything looks fine...
+ isRunning = true;
+ exitCode = 0;
+ }
+
+ if (!isRunning)
+ {
+ error("Emulator process is not running! Exit code:" + exitCode);
+ StringBuffer outBuf = null;
+ InputStream inStream = null;
+
+ int ch;
+
+ //Getting error output stream
+ String processAnswer = "";
+ inStream = p.getErrorStream();
+ outBuf = new StringBuffer();
+ try
+ {
+ while ((ch = inStream.read()) != -1)
+ {
+ outBuf.append((char) ch + "");
+ }
+ }
+ catch (IOException e)
+ {
+ error("Cannot read error output stream from Emulator proccess");
+ }
+
+ processAnswer = outBuf.toString();
+
+ if (processAnswer.length() == 0)
+ {
+ //if no error came from process, get standard output stream
+ inStream = p.getInputStream();
+ outBuf = new StringBuffer();
+ try
+ {
+ while ((ch = inStream.read()) != -1)
+ {
+ outBuf.append((char) ch + "");
+ }
+ }
+ catch (IOException e)
+ {
+ error("Cannot read standard output stream from Emulator proccess");
+ }
+
+ processAnswer = outBuf.toString();
+
+ }
+ String msg = EmulatorNLS.EXC_AndroidEmulatorStarter_ProcessTerminated;
+ msg += processAnswer;
+ throw new InstanceStartException(msg);
+ }
+
+ }
+
+ /**
+ * Kill the communication channel
+ *
+ * @param instance Android instance
+ */
+ public static void kill(IAndroidLogicInstance instance)
+ {
+ if (instance instanceof ISerialNumbered)
+ {
+ String serialNumber = ((ISerialNumbered) instance).getSerialNumber();
+ DDMSFacade.kill(serialNumber);
+ Process process = instance.getProcess();
+ if (process != null)
+ {
+ int tries = 0;
+ Integer exitValue = null;
+ while ((process != null) && (tries < 10) && (exitValue == null))
+ {
+ try
+ {
+ exitValue = process.exitValue();
+ }
+ catch (Throwable t)
+ {
+ tries++;
+ try
+ {
+ Thread.sleep(250);
+ }
+ catch (InterruptedException e)
+ {
+ //do nothing
+ }
+ }
+ }
+ process.destroy();
+ instance.setProcess(null);
+ }
+ }
+ }
+
+ /**
+ * Get the VNC port forward
+ *
+ * @param serial port number
+ * @return VNC port
+ */
+ public static int getVncServerPortFoward(String serial)
+ {
+ if (serial == null)
+ {
+ return 0;
+ }
+
+ int stringSize = serial.length();
+ String lastTwoNumbers = serial.substring(stringSize - 2, stringSize);
+
+ int port = INITIAL_VNC_PORT_VALUE;
+
+ try
+ {
+ port += Integer.valueOf(lastTwoNumbers);
+ }
+ catch (NumberFormatException e)
+ {
+ // do nothing (this should not happen)
+ }
+
+ return port;
+
+ }
+
+ public static int getEmulatorPort(String serial)
+ {
+ if (serial == null)
+ {
+ return 0;
+ }
+
+ int stringSize = serial.length();
+ String lastFourNumbers = serial.substring(stringSize - 4, stringSize);
+
+ int port = 0;
+
+ try
+ {
+ port = Integer.valueOf(lastFourNumbers);
+ }
+ catch (NumberFormatException e)
+ {
+ // do nothing (this should not happen)
+ }
+ return port;
+ }
+
+ /**
+ * Checks if the Device is still online...
+ * If the device is not online it is not possible to communicate with it.
+ * Notice it is a verification of the status of the Device wich may be different than the status of the Tml Instance...
+ *
+ * @param serialNumber serial number of the device
+ *
+ * @throws AndroidException If the device is not started
+ */
+ public static void testDeviceStatus(String serialNumber) throws AndroidException
+ {
+ if (!DDMSFacade.isDeviceOnline(serialNumber))
+ {
+ info("Device is offline: " + serialNumber);
+
+ throw new AndroidException(EmulatorNLS.EXC_AndroidLogicUtils_DeviceIsOffline);
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/ConnectVncLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/ConnectVncLogic.java
new file mode 100644
index 0000000..37014db
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/ConnectVncLogic.java
@@ -0,0 +1,227 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.logic;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.vnc.protocol.PluginProtocolActionDelegate;
+import org.eclipse.sequoyah.vnc.protocol.lib.IProtocolExceptionHandler;
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * This class contains the logic to stablish VNC connections
+ *
+ */
+public class ConnectVncLogic implements IAndroidLogic
+{
+ /**
+ * The port that is used to start the communication with the instance.
+ * It corresponds to the VNC display 1 port
+ */
+ private static final String LOCALHOST_IP_ADDRESS = "127.0.0.1";
+
+ public IJobChangeEvent vncServerDoneEvent = null;
+
+ /**
+ * Initialize by connecting to VNC
+ *
+ * @see com.motorola.studio.android.emulator.logic.IAndroidLogic#execute(IAndroidLogicInstance, int, IProgressMonitor)
+ */
+ public void execute(final IAndroidLogicInstance instance, final int timeout,
+ IProgressMonitor monitor) throws StartTimeoutException, StartCancelledException,
+ InstanceStartException
+ {
+ connectVnc(instance, timeout, monitor);
+ }
+
+ /**
+ * Connect to VNC
+ *
+ * @param instance instance to connect
+ * @param timeout timeout for the operation
+ * @param monitor monitor for this operation
+ *
+ * @throws InstanceStartException
+ */
+ private void connectVnc(IAndroidLogicInstance instance, int timeout, IProgressMonitor monitor)
+ throws StartTimeoutException, StartCancelledException, InstanceStartException
+ {
+ info("Trying to estabilish vnc connection with " + instance.getName());
+ long timeoutLimit = System.currentTimeMillis() + timeout;
+ try
+ {
+ startProtocol(instance, timeoutLimit, AndroidLogicUtils.getVncServerPortFoward(instance
+ .getInstanceIdentifier()), monitor);
+ }
+ catch (StartTimeoutException ise)
+ {
+ info("The protocol or the emulator services could not be launched. Stopping the instance.");
+ throw ise;
+ }
+ info("VNC Protocol is running for " + instance.getName());
+ }
+
+ /**
+ * Starts protocol connection
+ *
+ * @param instance The Android device instance
+ * @param timeoutLimit The timestamp of the time when timeout happens
+ * @param instanceHost The IP address of the started emulator instance
+ * @param monitor A progress monitor that will give the user feedback about this
+ * long running operation
+ *
+ * @throws InstanceStartException If some fatal error occurs during the start process,
+ * that may require status update at the clients
+ * @throws StartCancelledException If the user presses the "Cancel" button at the progress monitor
+ * @throws InstanceStartException
+ */
+ private void startProtocol(IAndroidEmulatorInstance instance, long timeoutLimit, int port,
+ IProgressMonitor monitor) throws StartTimeoutException, StartCancelledException,
+ InstanceStartException
+ {
+ try
+ {
+ monitor.beginTask(EmulatorNLS.MON_AndroidEmulatorStarter_ConnectingToEmulator, 100);
+ monitor.setTaskName(EmulatorNLS.MON_AndroidEmulatorStarter_ConnectingToEmulator);
+
+ testVncServer(instance);
+ AndroidLogicUtils.testCanceled(monitor);
+ requestStartProtocol(instance, port);
+ ProtocolHandle handle = instance.getProtocolHandle();
+
+ while (!PluginProtocolActionDelegate.isProtocolRunning(handle))
+ {
+ AndroidLogicUtils.testCanceled(monitor);
+
+ try
+ {
+ Thread.sleep(500);
+ }
+ catch (InterruptedException e1)
+ {
+ // Do nothing.
+ }
+
+ AndroidLogicUtils.testTimeout(timeoutLimit,
+ EmulatorNLS.EXC_AndroidEmulatorStarter_TimeoutWhileRunningProtocol);
+
+ }
+ monitor.worked(100);
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Set if the job has been completed
+ *
+ * @param jobEvent event job
+ */
+ public void setVncServerDoneEvent(IJobChangeEvent jobEvent)
+ {
+ this.vncServerDoneEvent = jobEvent;
+ }
+
+ /**
+ * Test if the VNC server is up and running
+ *
+ * @param instance emulator instance
+ *
+ * @throws InstanceStartException
+ */
+ private void testVncServer(final IAndroidEmulatorInstance instance)
+ throws InstanceStartException
+ {
+ if (vncServerDoneEvent != null)
+ {
+ IStatus jobResult = vncServerDoneEvent.getResult();
+ String reason = "";
+ if (IStatus.ERROR == jobResult.getSeverity())
+ {
+ reason = jobResult.getMessage();
+ }
+ else if (Status.CANCEL_STATUS.equals(jobResult))
+ {
+ reason = EmulatorNLS.INFO_ConnectVncLogic_UserCancelledVncServerStart;
+ }
+
+ String message =
+ NLS.bind(EmulatorNLS.EXC_VncServerNotRunning, new String[]
+ {
+ instance.getName(), reason
+ });
+ throw new InstanceStartException(message);
+ }
+ }
+
+ /**
+ * Starts the protocol execution, connecting to the server accessible through
+ * the provided Android Emulator instance
+ *
+ * @param androidInstance The Android device instance
+ */
+ private void requestStartProtocol(IAndroidEmulatorInstance androidInstance, int port)
+ throws InstanceStartException
+ {
+ if (androidInstance != null)
+ {
+ ProtocolHandle handle = null;
+
+ try
+ {
+ // Start protocol and screen update
+ info("Requesting protocol start");
+ Map<String, Object> parameters = new HashMap<String, Object>();
+ parameters.put("password", "");
+ parameters.put("bypassProxy", new Boolean(true));
+ IProtocolExceptionHandler excHandler = new AndroidExceptionHandler();
+ handle =
+ PluginProtocolActionDelegate.requestStartProtocolAsClient("vncProtocol38",
+ excHandler, LOCALHOST_IP_ADDRESS, port, parameters);
+
+ androidInstance.setProtocolHandle(handle);
+ }
+ catch (Exception e)
+ {
+ error("There is an error at the protocol specification.");
+ throw new InstanceStartException(EmulatorNLS.EXC_CouldNotStartProtocol);
+ }
+ }
+ else
+ {
+ error("Could not start the protocol, because the provided instance is null");
+ throw new InstanceStartException(EmulatorNLS.EXC_CouldNotStartProtocol);
+ }
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/ForwardVncPortLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/ForwardVncPortLogic.java
new file mode 100644
index 0000000..2884ff3
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/ForwardVncPortLogic.java
@@ -0,0 +1,55 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.logic;
+
+import java.io.IOException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+
+public class ForwardVncPortLogic implements IAndroidLogic
+{
+
+ public void execute(IAndroidLogicInstance instance, int timeout, IProgressMonitor monitor)
+ throws IOException, InstanceStartException
+ {
+ int port = AndroidLogicUtils.getVncServerPortFoward(instance.getInstanceIdentifier());
+
+ if (instance instanceof ISerialNumbered)
+ {
+ String serialNumber = ((ISerialNumbered) instance).getSerialNumber();
+ try
+ {
+ AndroidLogicUtils.testDeviceStatus(serialNumber);
+ }
+ catch (AndroidException e)
+ {
+ throw new InstanceStartException(e.getMessage());
+ }
+
+ boolean forwardOk = DDMSFacade.createForward(serialNumber, port, 5901);
+ if (!forwardOk)
+ {
+ throw new IOException("Could not forward VNC port 5901 to " + port + " on "
+ + instance.getName());
+ }
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/IAndroidLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/IAndroidLogic.java
new file mode 100644
index 0000000..d3028b2
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/IAndroidLogic.java
@@ -0,0 +1,30 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.logic;
+
+import java.io.IOException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
+
+public interface IAndroidLogic
+{
+ void execute(IAndroidLogicInstance instance, int timeout, IProgressMonitor monitor)
+ throws InstanceStartException, StartTimeoutException, StartCancelledException, IOException;
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/IAndroidLogicInstance.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/IAndroidLogicInstance.java
new file mode 100644
index 0000000..5f67716
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/IAndroidLogicInstance.java
@@ -0,0 +1,103 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.logic;
+
+import java.io.File;
+import java.util.List;
+import java.util.Properties;
+
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+
+/**
+ * DESCRIPTION:
+ * This class adds to the Android Emulator instance contract
+ * some specific methods related to services logic.
+ *
+ * RESPONSIBILITY:
+ * Define which additional information is required from a services to
+ * plug to the Android Emulator viewer.
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use the methods to retrieve information from a Android Emulator device instance
+ */
+public interface IAndroidLogicInstance extends IAndroidEmulatorInstance
+{
+ /**
+ * Get the command lines arguments for the instance
+ *
+ * @return list of command line arguments
+ */
+ String getCommandLineArguments();
+
+ /**
+ * Get the command lines arguments for the instance, returned as a Property object
+ *
+ * @return list of command line arguments
+ */
+ Properties getCommandLineArgumentsAsProperties();
+
+ /**
+ * Get the Start logic for this instance, which is the commands used to start the instance
+ *
+ * @return Start logic class
+ */
+ AbstractStartAndroidEmulatorLogic getStartLogic();
+
+ /**
+ * Check if there is a device connected to this instance
+ *
+ * @return true if there is a device connected, false otherwise
+ */
+ boolean hasDevice();
+
+ /**
+ * Get the reference to the File that point to the filesystem location where the
+ * user data of the VM is.
+ *
+ * @return the File object that references the filesystem location where the userdata
+ * of the given VM should be. Returns a null reference if SDK is not configured
+ * or if there is no VM with the given name.
+ */
+ File getUserdata();
+
+ /**
+ * Get the reference to the files in the VM folder that contain state data.
+ *
+ * @return File objects that reference files in the VM folder that contain state data.
+ */
+ List<File> getStateData();
+
+ /**
+ * Tests if there is a userdata file for this android device instance or if it is clean,
+ * i.e, there is no user data file for this Android Device Instance.
+ *
+ * @return True if there is no working copy at that location; false otherwise
+ */
+ boolean isClean();
+
+ /**
+ * Get the timeout value to start the instance
+ *
+ * @return timeout value
+ */
+ int getTimeout();
+
+ File getSnapshotOriginalFilePath();
+
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/StartEmulatorProcessLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/StartEmulatorProcessLogic.java
new file mode 100644
index 0000000..e71eba0
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/StartEmulatorProcessLogic.java
@@ -0,0 +1,523 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.logic;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.net.proxy.IProxyData;
+import org.eclipse.core.net.proxy.IProxyService;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.IViewPart;
+import org.osgi.framework.ServiceReference;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
+import com.motorola.studio.android.emulator.device.IDevicePropertiesConstants;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.nativeos.IDevicePropertiesOSConstants;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+@SuppressWarnings("restriction")
+public class StartEmulatorProcessLogic implements IAndroidLogic
+{
+ /**
+ *
+ */
+ private static final String EMULATOR_NO_SNAPSHOT_LOAD = "-no-snapshot-load";
+
+ /**
+ *
+ */
+ private static final String EMULATOR_NO_SNAPSHOT_SAVE = "-no-snapshot-save";
+
+ /**
+ *
+ */
+ private static final String EMULATOR_HTTP_PROXY_PARAMETER = "-http-proxy";
+
+ // Proxy constants
+ private static final String PROXY_AT = "@";
+
+ private static final String PROXY_COLON = ":";
+
+ private static final String PROXY_HTTP = "http://";
+
+ private static final String EMULATOR_VIEW = "com.motorola.studio.android.emulator.androidView";
+
+ // Strings used to build the command line for launching the emulator process
+ private static final String ARM_EMULATOR_RELATIVE_PATH = "/tools/emulator-arm";
+
+ private static final String x86_EMULATOR_RELATIVE_PATH = "/tools/emulator-x86";
+
+ private static final String EMULATOR_RELATIVE_PATH = "/tools/emulator";
+
+ private static final String EMULATOR_VM_PARAMETER = "-avd";
+
+ private static String selectedEmulatorPath = "";
+
+ public void execute(final IAndroidLogicInstance instance, int timeout, IProgressMonitor monitor)
+ throws InstanceStartException, StartTimeoutException, StartCancelledException
+ {
+
+ long timeoutLimit = AndroidLogicUtils.getTimeoutLimit(timeout);
+
+ info("Starting the Android Emulator process: " + instance);
+ instance.setWindowHandle(0);
+
+ File userData = instance.getUserdata();
+
+ if (userData != null)
+ {
+ File userdataDir = userData.getParentFile();
+ if ((userdataDir != null) && (!userdataDir.exists()))
+ {
+ userdataDir.mkdirs();
+ }
+ }
+
+ selectedEmulatorPath = retrieveEmulatorExecutableName(instance);
+
+ File emulatorExe = new File(SdkUtils.getSdkPath(), selectedEmulatorPath);
+
+ List<String> cmdList = new LinkedList<String>();
+
+ cmdList.add(emulatorExe.getAbsolutePath());
+ cmdList.add(EMULATOR_VM_PARAMETER);
+ cmdList.add(instance.getName());
+
+ Properties propArgs = instance.getCommandLineArgumentsAsProperties();
+ IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
+ String adtEmuOptions = store.getString(AdtPrefs.PREFS_EMU_OPTIONS);
+
+ StringTokenizer adtOptionsTokenizer = new StringTokenizer(adtEmuOptions, " ");
+ while (adtOptionsTokenizer.hasMoreTokens())
+ {
+ String nextToken = adtOptionsTokenizer.nextToken();
+ cmdList.add(nextToken);
+ }
+
+ for (Object key : propArgs.keySet())
+ {
+ String value = propArgs.getProperty(key.toString());
+
+ if (key.equals("other"))
+ {
+ StringTokenizer stringTokenizer = new StringTokenizer(value, " ");
+ while (stringTokenizer.hasMoreTokens())
+ {
+ cmdList.add(stringTokenizer.nextToken());
+ }
+ }
+ else
+ {
+ if ((value.trim().length() > 0) && !value.equals(Boolean.TRUE.toString()))
+ {
+ cmdList.add(key.toString());
+ if (Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ if (value.contains(" "))
+ {
+ value = "\"" + value + "\"";
+ }
+ }
+ else
+ {
+ if (value.contains("\\"))
+ {
+ value = value.replace("\\", "\\\\");
+ }
+
+ if (value.contains(" "))
+ {
+ value = value.replace(" ", "\\ ");
+ }
+ }
+
+ cmdList.add(value);
+ }
+ else if ((value.trim().length() > 0) && value.equals(Boolean.TRUE.toString()))
+ {
+ cmdList.add(key.toString());
+ }
+ }
+ }
+
+ // add proxy in case it is needed
+ Properties properties = instance.getProperties();
+ if (properties != null)
+ {
+ String useProxy =
+ properties.getProperty(IDevicePropertiesConstants.useProxy,
+ IDevicePropertiesConstants.defaultUseProxyValue);
+ if (Boolean.TRUE.toString().equals(useProxy))
+ {
+ addEmulatorProxyParameter(cmdList);
+ }
+ }
+
+ StringBuffer cmdLog = new StringBuffer("");
+
+ boolean httpProxyParamFound = false;
+ boolean logHttpProxyUsage = false;
+ for (String param : cmdList)
+ {
+ // Do not log -http-proxy information
+ if (!httpProxyParamFound)
+ {
+ if (!param.equals(EMULATOR_HTTP_PROXY_PARAMETER))
+ {
+ if (param.startsWith(emulatorExe.getAbsolutePath()))
+ {
+ // do not log emulator full path
+ cmdLog.append(selectedEmulatorPath + " ");
+ }
+ else
+ {
+ cmdLog.append(param + " ");
+ }
+ }
+ else
+ {
+ httpProxyParamFound = true;
+ logHttpProxyUsage = true;
+ }
+ }
+ else
+ {
+ httpProxyParamFound = false;
+ }
+ }
+
+ // Append http proxy usage to log
+ if (logHttpProxyUsage)
+ {
+ cmdLog.append("\nProxy settings are being used by the started emulator (-http-proxy parameter).");
+ }
+ // add command to not start from snapshot
+ if (properties != null)
+ {
+ String startFromSnapshot =
+ properties.getProperty(IDevicePropertiesConstants.startFromSnapshot,
+ IDevicePropertiesConstants.defaultstartFromSnapshotValue);
+ if (Boolean.FALSE.toString().equals(startFromSnapshot))
+ {
+ cmdList.add(EMULATOR_NO_SNAPSHOT_LOAD);
+ }
+ }
+
+ // Add the command to not save snapshot
+ if (properties != null)
+ {
+ String saveSnapshot =
+ properties.getProperty(IDevicePropertiesConstants.saveSnapshot,
+ IDevicePropertiesConstants.defaulSaveSnapshot);
+ if (Boolean.FALSE.toString().equals(saveSnapshot))
+ {
+ cmdList.add(EMULATOR_NO_SNAPSHOT_SAVE);
+ }
+ }
+
+ Process p;
+ try
+ {
+ p = AndroidLogicUtils.executeProcess(cmdList.toArray(new String[0]), cmdLog.toString());
+ }
+ catch (AndroidException e)
+ {
+ throw new InstanceStartException(e);
+ }
+ info("Wait until and emulator with the VM " + instance.getName() + " is up ");
+
+ AndroidLogicUtils.testProcessStatus(p);
+ instance.setProcess(p);
+ instance.setComposite(null);
+
+ final String avdName = instance.getName();
+
+ if (!Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ Collection<IViewPart> openedAndroidViews =
+ EclipseUtils.getAllOpenedViewsWithId(EMULATOR_VIEW);
+
+ if (!openedAndroidViews.isEmpty())
+ {
+ Runnable runnable = new Runnable()
+ {
+
+ public void run()
+ {
+ long windowHandle = -1;
+ long timeOutToFindWindow = System.currentTimeMillis() + 30000;
+
+ do
+ {
+ try
+ {
+ Thread.sleep(250);
+ }
+ catch (InterruptedException e)
+ {
+ // do nothing
+ }
+
+ try
+ {
+ AndroidLogicUtils.testTimeout(timeOutToFindWindow, "");
+ }
+ catch (StartTimeoutException e)
+ {
+ debug("Emulator window could not be found, instance :" + avdName);
+ break;
+ }
+
+ try
+ {
+ int port =
+ AndroidLogicUtils.getEmulatorPort(DDMSFacade
+ .getSerialNumberByName(instance.getName()));
+ if (port > 0)
+ {
+ windowHandle =
+ NativeUIUtils.getWindowHandle(instance.getName(), port);
+ }
+
+ }
+ catch (Exception t)
+ {
+ t.getCause().getMessage();
+ System.out.println(t.getCause().getMessage());
+ }
+ }
+ while (windowHandle <= 0);
+
+ if (windowHandle > 0)
+ {
+ instance.setWindowHandle(windowHandle);
+ NativeUIUtils.hideWindow(windowHandle);
+ }
+ }
+ };
+ Thread getHandleThread = new Thread(runnable, "Window Handle Thread");
+ getHandleThread.start();
+ }
+ }
+
+ if (instance.getProperties()
+ .getProperty(IDevicePropertiesOSConstants.useVnc, NativeUIUtils.getDefaultUseVnc())
+ .equals(Boolean.TRUE.toString()))
+ {
+ do
+ {
+ try
+ {
+ Thread.sleep(450);
+ }
+ catch (InterruptedException e)
+ {
+ // do nothing
+ }
+
+ AndroidLogicUtils.testCanceled(monitor);
+ try
+ {
+ AndroidLogicUtils.testTimeout(timeoutLimit,
+ NLS.bind(EmulatorNLS.EXC_TimeoutWhileStarting, avdName));
+ }
+ catch (StartTimeoutException e)
+ {
+ debug("Emulator start timeout has been reached, instance :" + avdName
+ + " has device: " + instance.hasDevice() + "isOnline? "
+ + DDMSFacade.isDeviceOnline(DDMSFacade.getSerialNumberByName(avdName)));
+ throw e;
+ }
+ }
+ while (!isEmulatorReady(avdName));
+
+ }
+
+ Thread t = new Thread("Process Error")
+ {
+ @Override
+ public void run()
+ {
+ boolean shouldTryAgain = true;
+ for (int i = 0; (i < 90) && shouldTryAgain; i++)
+ {
+ try
+ {
+ sleep(500);
+ Process p = instance.getProcess();
+ if (p != null)
+ {
+ AndroidLogicUtils.testProcessStatus(p);
+ }
+ }
+ catch (Exception e)
+ {
+ StudioLogger.info(StartEmulatorProcessLogic.class,
+ "Trying to stop the emulator process: execution stopped too early");
+ DialogWithToggleUtils.showError(
+ EmulatorPlugin.EMULATOR_UNEXPECTEDLY_STOPPED,
+ EmulatorNLS.GEN_Error, NLS.bind(
+ EmulatorNLS.ERR_AndroidLogicPlugin_EmulatorStopped,
+ instance.getName()));
+ shouldTryAgain = false;
+ try
+ {
+ instance.stop(true);
+ }
+ catch (InstanceStopException ise)
+ {
+ StudioLogger.error(StartEmulatorProcessLogic.class,
+ "Error trying to stop instance on process error", ise);
+ }
+ }
+ }
+ }
+ };
+ t.start();
+
+ debug("Emulator instance is now up and running... " + instance);
+ }
+
+ /**
+ * retrives the emulator executable name according to abi type property
+ *
+ * @param instance
+ * @return
+ */
+ private static String retrieveEmulatorExecutableName(IAndroidLogicInstance instance)
+ {
+ String emulatorPath = null;
+
+ Properties prop = instance.getProperties();
+ String abiType = prop.getProperty("Abi_Type");
+
+ if ((abiType == null) || (abiType.equals("")))
+ {
+ emulatorPath = EMULATOR_RELATIVE_PATH;
+ }
+ else if (abiType.toLowerCase().contains("arm"))
+ {
+ emulatorPath = ARM_EMULATOR_RELATIVE_PATH;
+ }
+ else
+ {
+ emulatorPath = x86_EMULATOR_RELATIVE_PATH;
+ }
+
+ File emulatorExe = new File(SdkUtils.getSdkPath(), emulatorPath + ".exe");
+
+ if (!emulatorExe.exists())
+ {
+ emulatorPath = EMULATOR_RELATIVE_PATH;
+ }
+
+ return emulatorPath;
+ }
+
+ /**
+ * Retrieve the Proxy service.
+ *
+ * @return IProxyService instance.
+ */
+ @SuppressWarnings({
+ "rawtypes", "unchecked"
+ })
+ private IProxyService retrieveProxyService()
+ {
+ IProxyService proxyService = null;
+
+ ServiceReference service =
+ EmulatorPlugin.getDefault().getBundle().getBundleContext()
+ .getServiceReference(IProxyService.class.getCanonicalName());
+ if (service != null)
+ {
+ proxyService =
+ (IProxyService) EmulatorPlugin.getDefault().getBundle().getBundleContext()
+ .getService(service);
+ }
+
+ return proxyService;
+ }
+
+ /**
+ * Add the http-proxy parameter to the emulator command line.
+ *
+ * @param cmdList
+ * List holding the commands to be called.
+ */
+ private void addEmulatorProxyParameter(List<String> cmdList)
+ {
+ IProxyService proxyService = retrieveProxyService();
+ if (proxyService != null)
+ {
+ String host = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE).getHost();
+ int port = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE).getPort();
+ boolean isAuthenticationRequired =
+ proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE)
+ .isRequiresAuthentication();
+ String userId = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE).getUserId();
+ String password = proxyService.getProxyData(IProxyData.HTTP_PROXY_TYPE).getPassword();
+
+ // there must be a host in order to access via proxy
+ if (host != null)
+ {
+ cmdList.add(EMULATOR_HTTP_PROXY_PARAMETER);
+
+ // add proxy info to the command list - authentication needed
+ if (isAuthenticationRequired)
+ {
+ cmdList.add(PROXY_HTTP + userId + PROXY_COLON + password + PROXY_AT + host
+ + PROXY_COLON + Integer.valueOf(port).toString());
+ }
+ // add proxy info to the command list - no authentication needed
+ else
+ {
+ cmdList.add(PROXY_HTTP + host + PROXY_COLON + Integer.valueOf(port).toString());
+ }
+ }
+ }
+ }
+
+ private boolean isEmulatorReady(String avdName)
+ {
+ String serialNum = DDMSFacade.getSerialNumberByName(avdName);
+ return DDMSFacade.isDeviceOnline(serialNum);
+ }
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/StartVncServerLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/StartVncServerLogic.java
new file mode 100644
index 0000000..b28cc9a
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/StartVncServerLogic.java
@@ -0,0 +1,229 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.logic;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.IJobChangeListener;
+import org.eclipse.core.runtime.jobs.IJobManager;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.adt.ISerialNumbered;
+import com.motorola.studio.android.common.exception.AndroidException;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+
+/**
+ * This class contains the logic to start the VNC server on the given Emulator.
+ */
+public final class StartVncServerLogic implements IAndroidLogic
+{
+ public static final String VNC_SERVER_JOB_PREFIX = "VNC Server - ";
+
+ public static final Object VNC_SERVER_JOB_FAMILY = new Object();
+
+ /**
+ * Sequence of commands that must be executed on the emulator to start the VNC server
+ */
+ private final Collection<String> remoteCommands = new LinkedList<String>();
+
+ /**
+ * Collection of listeners for the job executing the VNC server.
+ */
+ private final Collection<IJobChangeListener> listeners = new LinkedList<IJobChangeListener>();
+
+ /**
+ * Executes the logic to start the vnc server.
+ */
+ public void execute(final IAndroidLogicInstance instance, int timeout,
+ final IProgressMonitor monitor) throws InstanceStartException, StartTimeoutException,
+ StartCancelledException, IOException
+ {
+ cancelCurrentVncServerJobs(instance);
+
+ // Creates and starts a job that will keep running as long as the VNC server is up on that Emulator instance.
+ // add listeners that will receive notifications about the Job life-cycle.
+ VncServerJob vncServerJob = new VncServerJob(instance, getRemoteCommands());
+ for (IJobChangeListener vncServerListener : listeners)
+ {
+ vncServerJob.addJobChangeListener(vncServerListener);
+ }
+ vncServerJob.schedule();
+
+ }
+
+ /**
+ * Cancel any VncServerJob that is currently running the VNC server on the given emulator instance.
+ * @param instance, the emulator instances where VNC server execution must be canceled.
+ **/
+ public static void cancelCurrentVncServerJobs(IAndroidEmulatorInstance instance)
+ {
+ // stop the previous VNC Server job for this instance if any...
+ IJobManager manager = Job.getJobManager();
+ Job[] allVncJobs = manager.find(StartVncServerLogic.VNC_SERVER_JOB_FAMILY);
+ if (allVncJobs.length > 0)
+ {
+ for (Job job : allVncJobs)
+ {
+ if (job.getName().equals(
+ StartVncServerLogic.VNC_SERVER_JOB_PREFIX + instance.getName()))
+ {
+ info("Cancel execution of the VNC Server on " + instance);
+ job.cancel();
+ }
+ }
+ }
+ }
+
+ /**
+ * Add job listener to receive state-change notifications from the job that runs the VNC Server.
+ * @param vncServerListener job listener that willl receive state change notifications from the VNC Serever job.
+ */
+ public void addVncServerJobListener(IJobChangeListener vncServerListener)
+ {
+ listeners.add(vncServerListener);
+ }
+
+ /**
+ * Add a command to be executed in the process of starting the VNC Server on the Emulator.
+ * @param remoteCommand
+ */
+ public void addRemoteCommand(String remoteCommand)
+ {
+ remoteCommands.add(remoteCommand);
+ }
+
+ /**
+ * Get the list of commands to be executed on the Emulator in order to start the VNC Server.
+ * @return the sequence of commands that must be executed on the Emulator to start the VNC Server.
+ */
+ public Collection<String> getRemoteCommands()
+ {
+ return remoteCommands;
+ }
+
+}
+
+/**
+ * Job that executes the VNC Server.
+ * It will keep running as long as the VNC Server process is running on the Emulator.
+ */
+class VncServerJob extends Job implements ISchedulingRule
+{
+ private String serialNumber;
+
+ /**
+ * Sequence of commands that must be executed on the emulator to start the VNC server
+ */
+ private final Collection<String> remoteCommands;
+
+ /**
+ * Creates a new job to execute the VNC server on the given emulator instance.
+ * @param instance, emulator instance where the VNC server will be started.
+ * @param remoteCommands, sequence of commands that must be executed on the given emulator instance to start the VNC Server.
+ * @throws InstanceStartException
+ */
+ public VncServerJob(IAndroidLogicInstance instance, Collection<String> remoteCommands)
+ throws InstanceStartException
+ {
+ super(StartVncServerLogic.VNC_SERVER_JOB_PREFIX + instance.getName());
+
+ this.serialNumber = ((ISerialNumbered) instance).getSerialNumber();
+
+ try
+ {
+ AndroidLogicUtils.testDeviceStatus(serialNumber);
+ }
+ catch (AndroidException e)
+ {
+ throw new InstanceStartException(e.getMessage());
+ }
+
+ this.remoteCommands = remoteCommands;
+ setSystem(true);
+ setRule(this);
+ }
+
+ /**
+ * @see org.eclipse.core.runtime.jobs.Job#run(IProgressMonitor)
+ */
+ @Override
+ public IStatus run(IProgressMonitor monitor)
+ {
+ IStatus status = Status.OK_STATUS;
+ try
+ {
+ info("Executing VNC Server on " + serialNumber);
+ AndroidLogicUtils.testDeviceStatus(serialNumber);
+ DDMSFacade.execRemoteApp(serialNumber, remoteCommands, monitor);
+
+ if (monitor.isCanceled())
+ {
+ status = Status.CANCEL_STATUS;
+ }
+ }
+ catch (Exception e)
+ {
+ String errorMessage = "Error while trying to run the VNC server on " + serialNumber;
+ error(errorMessage + " " + e.getMessage());
+ status = new Status(IStatus.CANCEL, EmulatorPlugin.PLUGIN_ID, errorMessage, e);
+ }
+
+ info("Finished the execution of the VNC Server on " + serialNumber + " with status "
+ + status);
+
+ return status;
+ }
+
+ /**
+ * @see org.eclipse.core.runtime.jobs.Job#belongsTo(Object)
+ */
+ @Override
+ public boolean belongsTo(Object family)
+ {
+ return StartVncServerLogic.VNC_SERVER_JOB_FAMILY.equals(family);
+ }
+
+ public boolean contains(ISchedulingRule rule)
+ {
+ boolean contains = false;
+ if (rule instanceof VncServerJob)
+ {
+ VncServerJob otherVncServerJob = (VncServerJob) rule;
+ contains = otherVncServerJob.serialNumber.equals(serialNumber);
+ }
+
+ return contains;
+ }
+
+ public boolean isConflicting(ISchedulingRule rule)
+ {
+ return contains(rule);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/TransferFilesLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/TransferFilesLogic.java
new file mode 100644
index 0000000..9df8eb6
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/TransferFilesLogic.java
@@ -0,0 +1,97 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.logic;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+public class TransferFilesLogic implements IAndroidLogic
+{
+ private String localDir = "";
+
+ private String remoteDir = "";
+
+ private final Collection<String> filenames = new LinkedList<String>();
+
+ public void execute(IAndroidLogicInstance instance, int timeout, IProgressMonitor monitor)
+ throws InstanceStartException, StartCancelledException, StartTimeoutException,
+ IOException
+ {
+ if ((instance != null) && (timeout > 0) && (localDir != null) && (!"".equals(localDir))
+ && (remoteDir != null) && ("".equals(remoteDir)))
+ {
+ error("Cannot transfer files because the parameters provided are not as expected.");
+ throw new InstanceStartException(
+ EmulatorNLS.ERR_TransferFilesLogic_NotEnoughInformation);
+ }
+
+ String serialNumber = DDMSFacade.getSerialNumberByName(instance.getName());
+ IStatus status =
+ DDMSFacade.pushFiles(serialNumber, getLocalDir(), getFilenames(), getRemoteDir(),
+ timeout, monitor, null);
+ if (status.getSeverity() == IStatus.CANCEL)
+ {
+ throw new StartCancelledException();
+ }
+ else if (status.getSeverity() == IStatus.ERROR)
+ {
+ throw new InstanceStartException(status.getMessage());
+ }
+ }
+
+ public void setLocalDir(String localDir)
+ {
+ this.localDir = localDir;
+ }
+
+ public String getLocalDir()
+ {
+ return localDir;
+ }
+
+ public void addFilename(String filename)
+ {
+ filenames.add(filename);
+ }
+
+ public Collection<String> getFilenames()
+ {
+ return filenames;
+ }
+
+ public void setRemoteDir(String remoteDir)
+ {
+ this.remoteDir = remoteDir;
+ }
+
+ public String getRemoteDir()
+ {
+ return remoteDir;
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/reset/AndroidEmulatorReseter.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/reset/AndroidEmulatorReseter.java
new file mode 100644
index 0000000..4f0ad44
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/reset/AndroidEmulatorReseter.java
@@ -0,0 +1,189 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.logic.reset;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+
+/**
+ * DESCRIPTION:
+ * This class contains the business layer of the Android
+ * Emulator reset procedure
+ *
+ * RESPONSIBILITY:
+ * Reset any Android Emulator
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use the public method to reset a Android Emulator
+ */
+public class AndroidEmulatorReseter
+{
+ /**
+ * Resets a Android Emulator based at the provided folder
+ *
+ * @param userDataFolder The folder where a working copy of the emulator is located
+ * @param force Perform the reset without questioning the user
+ *
+ * @return IStatus the status of the operation
+ */
+
+ static String SNAPSHOT_FILE_NAME = "snapshots.img";
+
+ public static IStatus resetInstance(IAndroidLogicInstance instance)
+ {
+ IStatus resetStatus = Status.OK_STATUS;
+
+ File userData = instance.getUserdata();
+ List<File> stateData = instance.getStateData();
+
+ if ((userData != null) || (stateData != null))
+ {
+ List<File> allFiles = new ArrayList<File>();
+ if (stateData != null)
+ {
+ allFiles.addAll(stateData);
+ }
+ if (userData != null)
+ {
+ allFiles.add(userData);
+ }
+
+ for (File file : allFiles)
+ {
+ if (file.exists())
+ {
+ if (!file.delete())
+ {
+ error("There was an error when trying to remove the emulator instance user data files");
+ resetStatus =
+ new Status(
+ IStatus.ERROR,
+ EmulatorPlugin.PLUGIN_ID,
+ NLS.bind(
+ EmulatorNLS.EXC_AndroidEmulatorReseter_ErrorWhilePerformingDeleteOperation,
+ new Path(file.getPath()).removeLastSegments(1)
+ .toString()));
+ break;
+ }
+ }
+ }
+
+ // When the snapshots file is missing or corrupted after the reset, the snapshot operation will not work properly
+ // (when start and then closing the AVD after a reset operation,
+ /// the snapshots file will not be saved), that is why the error message should be shown.
+
+ if ((allFiles != null) && (allFiles.size() > 0))
+ {
+ File snapshot = instance.getSnapshotOriginalFilePath();
+
+ String snapshotToPath =
+ allFiles.get(0).getParentFile() + File.separator + SNAPSHOT_FILE_NAME;
+
+ File snapshotToFile = new File(snapshotToPath);
+
+ if ((snapshot != null) && (snapshotToFile.exists()))
+ {
+
+ BufferedInputStream bis = null;
+ BufferedOutputStream bos = null;
+
+ try
+ {
+ bis = new BufferedInputStream((new FileInputStream(snapshot)));
+ bos = new BufferedOutputStream(new FileOutputStream(snapshotToFile));
+
+ int c;
+ while ((c = bis.read()) >= 0)
+ {
+ bos.write(c);
+ }
+ }
+ catch (Exception e)
+ {
+ error("Error while copying the original snapshot file to the avd that is being reseted:"
+ + e.getMessage());
+ if (resetStatus.equals(Status.OK_STATUS))
+ {
+ resetStatus =
+ new Status(
+ IStatus.ERROR,
+ EmulatorPlugin.PLUGIN_ID,
+ NLS.bind(
+ EmulatorNLS.EXC_AndroidEmulatorReseter_ErrorWhilePerformingSnapshotCopyOperation,
+ snapshot.getPath(), allFiles.get(0)
+ .getParentFile()));
+ }
+ else
+ {
+
+ resetStatus =
+ new Status(
+ IStatus.ERROR,
+ EmulatorPlugin.PLUGIN_ID,
+ NLS.bind(
+ EmulatorNLS.EXC_AndroidEmulatorReseter_ErrorWhilePerformingDeleteSnapshotOperation,
+ allFiles.get(0).getParentFile(),
+ snapshot.getPath()));
+ }
+ }
+ finally
+ {
+ try
+ {
+ if (bis != null)
+ {
+ bis.close();
+ }
+
+ if (bos != null)
+ {
+ bos.close();
+ }
+ }
+ catch (Exception e)
+ {
+ error("Error while closing the snapshots file of the avd that is being reseted:"
+ + e.getMessage());
+ }
+ }
+
+ }
+ }
+
+ }
+
+ return resetStatus;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/start/AndroidEmulatorStarter.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/start/AndroidEmulatorStarter.java
new file mode 100644
index 0000000..cc46a21
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/start/AndroidEmulatorStarter.java
@@ -0,0 +1,173 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.logic.start;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.InstanceStartException;
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.exception.StartTimeoutException;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic;
+import com.motorola.studio.android.emulator.logic.AndroidLogicUtils;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic.LogicMode;
+
+/**
+ * DESCRIPTION:
+ * This class contains the business layer of the Android
+ * Emulator start procedure
+ *
+ * RESPONSIBILITY:
+ * Start any Android Emulator
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use the public method to start a Android Emulator
+ */
+public class AndroidEmulatorStarter
+{
+
+ /**
+ * Starts this instance, after creating a clean VM copy at the provided location.
+ * Besides that, adds a new Android Emulator viewer inside the Android view.
+ * This method provides automatic VM startup.
+ *
+ * @param instance The Android device instance
+ * @param monitor A progress monitor that will give the user feedback about this
+ * long running operation
+ *
+ * @return the status of the operation (OK, Cancel or Error+ErrorMessage)
+ */
+ public static IStatus startInstance(IAndroidLogicInstance instance,
+ Map<Object, Object> arguments, IProgressMonitor monitor)
+ {
+ if (instance == null)
+ {
+ error("Abort start operation. Instance is null.");
+ return new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID,
+ EmulatorNLS.ERR_AndroidEmulatorStarter_InstanceNullPointer);
+ }
+
+ if (monitor == null)
+ {
+ monitor = new NullProgressMonitor();
+ }
+
+ IStatus status = Status.OK_STATUS;
+
+ if (!instance.isStarted())
+ {
+ int timeout = instance.getTimeout();
+ if (timeout <= 0)
+ {
+ status =
+ new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID, NLS.bind(
+ EmulatorNLS.ERR_AndroidLogicPlugin_InvalidTimeoutValue,
+ new Object[]
+ {
+ timeout
+ }));
+ return status;
+ }
+
+ try
+ {
+ try
+ {
+ LogicMode mode = LogicMode.START_MODE;
+ if (arguments != null)
+ {
+ Object modeObject = arguments.get(LogicMode.class);
+ if (modeObject instanceof LogicMode)
+ {
+ mode = (LogicMode) modeObject;
+ }
+ }
+
+ AbstractStartAndroidEmulatorLogic logic = instance.getStartLogic();
+ if (logic != null)
+ {
+ debug("Retrieved start logic: " + logic);
+ logic.execute(instance, mode, timeout, monitor);
+ AndroidLogicUtils.testCanceled(monitor);
+ }
+ else
+ {
+ error("Cannot start emulator because a logic is not provided for that.");
+ throw new InstanceStartException(
+ EmulatorNLS.ERR_AndroidEmulatorStarter_NoLogicAvailableForStart);
+ }
+ }
+ catch (Exception e)
+ {
+ error("An exception happened while trying to execute the start emulator logic for "
+ + instance + " Try to rollback by executing the stop process...");
+
+ try
+ {
+ instance.stop(true);
+ }
+ catch (InstanceStopException e1)
+ {
+ error("There was an error while forcing the stop the instance");
+ }
+
+ throw e;
+ }
+ }
+ catch (StartTimeoutException e)
+ {
+ error("A timeout has happeded during the start Android Emulator Instance. "
+ + instance + "Cause: " + e.getMessage());
+ status = new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID, e.getMessage(), e);
+ }
+ catch (InstanceStartException e)
+ {
+ error("It was not possible to start the Android Emulator Instance. " + instance
+ + "Cause: " + e.getMessage());
+ status = new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID, e.getMessage());
+ }
+ catch (StartCancelledException e)
+ {
+ info("Start operation was cancelled." + instance);
+ status = Status.CANCEL_STATUS;
+ }
+ catch (Exception e)
+ {
+ error("Unknown exception while starting the emulator instance: " + instance
+ + " Cause: " + e.getMessage());
+
+ status = new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID, e.getMessage());
+ }
+ }
+ return status;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/stop/AndroidEmulatorStopper.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/stop/AndroidEmulatorStopper.java
new file mode 100644
index 0000000..4f8395e
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/logic/stop/AndroidEmulatorStopper.java
@@ -0,0 +1,188 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.logic.stop;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.vnc.protocol.PluginProtocolActionDelegate;
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+import org.eclipse.sequoyah.vnc.vncviewer.registry.VNCProtocolRegistry;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.AndroidLogicUtils;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+
+/**
+ * DESCRIPTION:
+ * This class contains the business layer of the Android
+ * Emulator stop procedure
+ *
+ * RESPONSIBILITY:
+ * Stop any Android Emulator
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use the public method to stop a Android Emulator
+ */
+public class AndroidEmulatorStopper
+{
+ private static Lock lock = new ReentrantReadWriteLock().writeLock();
+
+ private static final int MAX_LOCK_ATTEMPTS = 20;
+
+ /**
+ * Stops this instance, removing its viewer from Android Emulator View
+ *
+ * @param instance The device instance
+ * @param force If true do not ask the user if he/she wants to proceed
+ * @param monitor A progress monitor that will show the disposal
+ * action progress at UI
+ *
+ * @return True if the instance was stopped; false if the user chose not to stop it
+ */
+ public static boolean stopInstance(final IAndroidLogicInstance instance, boolean force,
+ boolean kill, IProgressMonitor monitor)
+ {
+
+ if (instance == null)
+ {
+ error("Could not stop the protocol because the provided instance is null");
+ return false;
+ }
+
+ boolean canProceed = true;
+
+ // decision whether to actually stop or not comes from user
+ if (!force)
+ {
+
+ canProceed =
+ EclipseUtils.showQuestionDialog(EmulatorNLS.GEN_Question, NLS.bind(
+ EmulatorNLS.QUESTION_AndroidEmulatorStopper_StopEmulatorQuestion,
+ instance.getName()));
+
+ }
+
+ if (canProceed)
+ {
+ int attempts = 0;
+ boolean locked = lock.tryLock();
+ while (!locked && (attempts < MAX_LOCK_ATTEMPTS))
+ {
+ try
+ {
+ Thread.sleep(125);
+ }
+ catch (InterruptedException e)
+ {
+ //Do nothing!
+ }
+ locked = lock.tryLock();
+ attempts++;
+ }
+
+ if (locked)
+ {
+ if (monitor == null)
+ {
+ monitor = new NullProgressMonitor();
+ }
+
+ try
+ {
+ info("Stopping the Android Emulator instance");
+
+ monitor
+ .beginTask(EmulatorNLS.MON_AndroidEmulatorStopper_DisposingInstance,
+ 200);
+ monitor.setTaskName(EmulatorNLS.MON_AndroidEmulatorStopper_DisposingInstance);
+
+ // Try to stop the protocol.
+ //
+ // This is not critical to the stop instance procedure, but it is
+ // desirable because the TmL's methods may do some cleanup
+ // before returning. The loop below tries to stop for some time (and
+ // give enough time for cleanup as well), but if TmL does not finish
+ // in an acceptable time, the stop instance procedure continues
+
+ try
+ {
+ info("Trying to stop the protocol");
+ // Try to implement a TmL independent code
+ ProtocolHandle handle = instance.getProtocolHandle();
+ if (handle != null)
+ {
+ PluginProtocolActionDelegate.requestStopProtocol(handle);
+
+ while (PluginProtocolActionDelegate.isProtocolRunning(handle))
+ {
+ Thread.sleep(250);
+ }
+ VNCProtocolRegistry.getInstance().unregister(handle);
+ info("Protocol stopped");
+ }
+ }
+ catch (Exception e)
+ {
+ error("There was an error while trying to stop the protocol");
+ }
+
+ // Try to implement a TmL independent code
+ instance.setProtocolHandle(null);
+
+ monitor.worked(100);
+ monitor.setTaskName(EmulatorNLS.MON_AndroidEmulatorStopper_StopVm);
+
+ if (kill)
+ {
+ AndroidLogicUtils.kill(instance);
+ }
+
+ info("Stopped the Android Emulator instance");
+ }
+ finally
+ {
+ monitor.done();
+ try
+ {
+ lock.unlock();
+ }
+ catch (Exception e)
+ {
+ warn("The thread that is releasing the lock is not the one which has it.");
+ }
+ }
+ }
+ else
+ {
+ canProceed = false;
+ }
+ }
+
+ return canProceed;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/repair/RepairAvdHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/repair/RepairAvdHandler.java
new file mode 100644
index 0000000..0a0d6a3
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/repair/RepairAvdHandler.java
@@ -0,0 +1,107 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.service.repair;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.android.sdklib.internal.avd.AvdInfo;
+import com.android.sdklib.internal.avd.AvdInfo.AvdStatus;
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+import com.motorola.studio.android.emulator.device.refresh.InstancesListRefresh;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * This is responsible for repair damaged avds.
+ */
+public class RepairAvdHandler extends ServiceHandler
+{
+
+ /* (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#newInstance()
+ */
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new RepairAvdHandler();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#runService(org.eclipse.sequoyah.device.framework.model.IInstance, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> argumentos,
+ IProgressMonitor monitor)
+ {
+ IStatus status = Status.OK_STATUS;
+ if (!(instance instanceof AndroidDeviceInstance))
+ {
+ error(EmulatorNLS.RepairAvdHandler_Not_Android_Instance);
+
+ status =
+ new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID,
+ EmulatorNLS.ERR_StopEmulatorHandler_NotAnAndroidEmulator);
+ }
+ else
+ {
+ AndroidDeviceInstance androidDeviceInstance = (AndroidDeviceInstance) instance;
+ AvdInfo avdInfo = SdkUtils.getVm(androidDeviceInstance.getName());
+ if ((avdInfo != null) && isAvdRepairable(avdInfo.getStatus()))
+ {
+ status = SdkUtils.repairAvd(avdInfo);
+ if (status.getSeverity() != IStatus.OK)
+ {
+ error(getClass(), "IOException ocurred during repairAvd operation"); //$NON-NLS-1$
+ }
+ }
+ else
+ {
+ status =
+ new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID,
+ EmulatorNLS.RepairAvdHandler_AVD_NOT_REPAIRABLE);
+ }
+ }
+ if (status.getSeverity() == IStatus.OK)
+ {
+ InstancesListRefresh.refresh();
+ }
+ return status;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler#updatingService(org.eclipse.sequoyah.device.framework.model.IInstance, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public IStatus updatingService(IInstance arg0, IProgressMonitor arg1)
+ {
+ return Status.OK_STATUS;
+ }
+
+ private boolean isAvdRepairable(AvdStatus avdStatus)
+ {
+ return avdStatus == AvdStatus.ERROR_IMAGE_DIR;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/reset/ResetServiceHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/reset/ResetServiceHandler.java
new file mode 100644
index 0000000..9a9618e
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/reset/ResetServiceHandler.java
@@ -0,0 +1,124 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.service.reset;
+
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+import com.motorola.studio.android.emulator.logic.reset.AndroidEmulatorReseter;
+
+/**
+ * DESCRIPTION:
+ * This class plugs the reset procedure to a TmL service
+ *
+ * RESPONSIBILITY:
+ * Provide access to the reset feature from TmL device framework
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by Eclipse only
+ */
+public class ResetServiceHandler extends ServiceHandler
+{
+
+ private boolean userAgreed;
+
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new ResetServiceHandler();
+ }
+
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> arguments,
+ IProgressMonitor monitor)
+ {
+
+ boolean force = false;
+
+ if (arguments != null)
+ {
+ Object forceObj = arguments.get(EmulatorPlugin.FORCE_ATTR);
+ if (forceObj instanceof Boolean)
+ {
+ force = ((Boolean) forceObj).booleanValue();
+ }
+ }
+
+ IStatus status = Status.OK_STATUS;
+ if (!force && !userAgreed)
+ {
+ status = Status.CANCEL_STATUS;
+ }
+
+ if (status.isOK() && instance instanceof IAndroidLogicInstance)
+ {
+ status = AndroidEmulatorReseter.resetInstance((IAndroidLogicInstance) instance);
+ }
+
+ // Collecting usage data for statistical purposes
+ try
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_EMULATOR_RESET,
+ StudioLogger.KIND_EMULATOR, StudioLogger.DESCRIPTION_DEFAULT,
+ EmulatorPlugin.PLUGIN_ID, EmulatorPlugin.getDefault().getBundle().getVersion()
+ .toString());
+ }
+ catch (Throwable e)
+ {
+ //Do nothing, but error on the log should never prevent app from working
+ }
+ return status;
+ }
+
+ @Override
+ public IStatus updatingService(IInstance instance, IProgressMonitor monitor)
+ {
+ StudioLogger.info("Updating reset service");
+ return Status.OK_STATUS;
+ }
+
+ public IStatus singleInit(List<IInstance> instances)
+ {
+ int reset =
+ EclipseUtils.showInformationDialog(EmulatorNLS.GEN_Warning,
+ EmulatorNLS.QUESTION_AndroidEmulatorReseter_ConfirmationText, new String[]
+ {
+ EmulatorNLS.QUESTION_AndroidEmulatorReseter_Yes,
+ EmulatorNLS.QUESTION_AndroidEmulatorReseter_No
+ }, MessageDialog.WARNING);
+ userAgreed = reset == Dialog.OK;
+
+ return Status.OK_STATUS;
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/start/StartEmulatorHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/start/StartEmulatorHandler.java
new file mode 100644
index 0000000..a7b3085
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/start/StartEmulatorHandler.java
@@ -0,0 +1,128 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.service.start;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.AndroidLogicUtils;
+import com.motorola.studio.android.emulator.logic.start.AndroidEmulatorStarter;
+
+/**
+ * DESCRIPTION: This class handles the start Android Emulator action, which
+ * transitions from both stopped states to started
+ *
+ * RESPONSIBILITY: Delegate the action to the AndroidEmulatorStarter, which
+ * contains the business layer logic for starting Android Emulators.
+ *
+ * COLABORATORS: None.
+ *
+ * USAGE: This class is intended to be used by Eclipse only
+ */
+public class StartEmulatorHandler extends ServiceHandler
+{
+
+ /**
+ * @see IServiceHandler#newInstance()
+ */
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new StartEmulatorHandler();
+ }
+
+ /**
+ * @see ServiceHandler#runService(IInstance, Map, IProgressMonitor)
+ */
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> arguments,
+ IProgressMonitor monitor)
+ {
+ IStatus status = Status.OK_STATUS;
+ try
+ {
+
+ String description = "";
+
+ // Tests if the given instance is a Android instance
+ if (!(instance instanceof AndroidDeviceInstance))
+ {
+ error("Aborting start service. This is not an Android Emulator instance...");
+ status =
+ new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID,
+ EmulatorNLS.ERR_StartEmulatorHandler_NotAnAndroidEmulator);
+ }
+ else
+ {
+ description = ((AndroidDeviceInstance) instance).getTarget();
+ try
+ {
+ AndroidLogicUtils.testCanceled(monitor);
+
+ status =
+ AndroidEmulatorStarter.startInstance((AndroidDeviceInstance) instance,
+ arguments, monitor);
+
+ debug("Finished Execution of the start emulator service... :" + instance
+ + " Status => " + status);
+ }
+ catch (StartCancelledException e)
+ {
+ monitor.done();
+ status = Status.CANCEL_STATUS;
+ }
+ }
+
+ description = StudioLogger.KEY_TARGET + description;
+ StudioLogger.collectUsageData(StudioLogger.WHAT_EMULATOR_START,
+ StudioLogger.KIND_EMULATOR, description, EmulatorPlugin.PLUGIN_ID,
+ EmulatorPlugin.getDefault().getBundle().getVersion().toString());
+ }
+ catch (Throwable t)
+ {
+ StudioLogger.error(StartEmulatorHandler.class.toString(),
+ "An exception ocurred during emulator start up process.", t);
+ status =
+ new Status(Status.ERROR, EmulatorPlugin.PLUGIN_ID,
+ "An exception ocurred during emulator start up process.");
+ }
+ return status;
+ }
+
+ /**
+ * @see ServiceHandler#updatingService(IInstance, IProgressMonitor)
+ */
+ @Override
+ public IStatus updatingService(IInstance instance, IProgressMonitor monitor)
+ {
+ return Status.OK_STATUS;
+ }
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/stop/StopEmulatorCommand.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/stop/StopEmulatorCommand.java
new file mode 100644
index 0000000..60162eb
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/stop/StopEmulatorCommand.java
@@ -0,0 +1,47 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.service.stop;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.sequoyah.device.common.utilities.exception.SequoyahException;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+
+public class StopEmulatorCommand extends AbstractHandler
+{
+
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ if (instance instanceof IInstance)
+ {
+ try
+ {
+ EmulatorPlugin.getStopServiceHandler().run((IInstance) instance);
+ }
+ catch (SequoyahException e)
+ {
+ //do nothing
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/stop/StopEmulatorHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/stop/StopEmulatorHandler.java
new file mode 100644
index 0000000..b96706d
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/service/stop/StopEmulatorHandler.java
@@ -0,0 +1,130 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.service.stop;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent;
+import org.eclipse.sequoyah.device.framework.events.InstanceEventManager;
+import org.eclipse.sequoyah.device.framework.events.InstanceEvent.InstanceEventType;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.device.framework.model.handler.IServiceHandler;
+import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.device.instance.AndroidDeviceInstance;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.stop.AndroidEmulatorStopper;
+
+/**
+ * DESCRIPTION:
+ * This class plugs the stop procedure to a TmL service
+ *
+ * RESPONSIBILITY:
+ * Provide access to the stop feature from TmL device framework
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by Eclipse only
+ */
+public class StopEmulatorHandler extends ServiceHandler
+{
+ @Override
+ public IServiceHandler newInstance()
+ {
+ return new StopEmulatorHandler();
+ }
+
+ @Override
+ public IStatus runService(IInstance instance, Map<Object, Object> arguments,
+ IProgressMonitor monitor)
+ {
+ debug("Executing the stop emulator service... Instance:" + instance + " Arguments:"
+ + arguments);
+
+ IStatus status = Status.OK_STATUS;
+
+ // actually stop emulator
+ if (!(instance instanceof AndroidDeviceInstance))
+ {
+ error("Aborting start service. This is not an Android Emulator instance...");
+
+ status =
+ new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID,
+ EmulatorNLS.ERR_StopEmulatorHandler_NotAnAndroidEmulator);
+ }
+ else
+ {
+ boolean force = false;
+ if (arguments != null)
+ {
+ Object forceObj = arguments.get(EmulatorPlugin.FORCE_ATTR);
+ if (forceObj instanceof Boolean)
+ {
+ force = ((Boolean) forceObj).booleanValue();
+ }
+ }
+
+ boolean stopPerformed =
+ AndroidEmulatorStopper.stopInstance((AndroidDeviceInstance) instance, force,
+ true, monitor);
+
+ if (!stopPerformed)
+ {
+ // user decided not to stop; return a cancel status
+ status = Status.CANCEL_STATUS;
+ }
+ else
+ {
+ instance.setNameSuffix(null);
+ InstanceEventManager.getInstance().notifyListeners(
+ new InstanceEvent(InstanceEventType.INSTANCE_UPDATED, instance));
+ }
+ }
+
+ debug("Finished the execution of the stop emulator service: " + instance + " status: "
+ + status);
+
+ // Collecting usage data for statistical purposes
+ try
+ {
+ StudioLogger.collectUsageData(StudioLogger.WHAT_EMULATOR_STOP,
+ StudioLogger.KIND_EMULATOR, status.toString(), EmulatorPlugin.PLUGIN_ID,
+ EmulatorPlugin.getDefault().getBundle().getVersion().toString());
+ }
+ catch (Throwable e)
+ {
+ //Do nothing, but error on the log should never prevent app from working
+ }
+
+ return status;
+ }
+
+ @Override
+ public IStatus updatingService(IInstance instance, IProgressMonitor monitor)
+ {
+ return Status.OK_STATUS;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/AndroidSkin.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/AndroidSkin.java
new file mode 100644
index 0000000..2eff106
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/AndroidSkin.java
@@ -0,0 +1,428 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.skin.android;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.skin.AndroidSkinBean;
+import com.motorola.studio.android.emulator.core.skin.IAndroidKey;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.core.skin.ISkinKeyXmlTags;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.skin.android.parser.LayoutFileModel;
+import com.motorola.studio.android.emulator.skin.android.parser.LayoutFileParser;
+
+public class AndroidSkin implements IAndroidSkin
+{
+ /**
+ * The released images of the skin, as read from the skin files
+ */
+ private final Map<String, ImageData> releasedImagesPool = new HashMap<String, ImageData>();
+
+ /**
+ * The pressed images of the skin, as read from the skin files
+ */
+ private final Map<String, ImageData> pressedImagesPool = new HashMap<String, ImageData>();
+
+ /**
+ * The enter images of the skin, as read from the skin files
+ */
+ private final Map<String, ImageData> enterImagesPool = new HashMap<String, ImageData>();
+
+ /**
+ * The keys of the skin, as read from the skin files
+ */
+ private final Map<String, Collection<IAndroidKey>> androidKeysPool =
+ new HashMap<String, Collection<IAndroidKey>>();
+
+ /**
+ * The folder where to look for skin files
+ */
+ private File skinFilesPath;
+
+ /**
+ * A model containing the parsed layout file
+ */
+ private LayoutFileModel layoutFile;
+
+ private Properties keycodes;
+
+ /**
+ * Retrieves data being kept in a pool
+ *
+ * @param layoutName The name of the layout which object has been requested
+ * @param pool The pool where to look for data
+ *
+ * @return An object from the pool that matches the request
+ *
+ * @throws SkinException If either the layout file was not loaded, or if the images/keys cannot
+ * be generated
+ */
+ private ImageData getImageFromPool(String layoutName, Map<String, ImageData> pool)
+ throws SkinException
+ {
+ if (layoutFile == null)
+ {
+ error("User has tried to request skin data without setting a valid skin files path");
+ throw new SkinException(EmulatorNLS.ERR_AndroidSkin_NoLayoutLoaded);
+ }
+
+ // Tries to get data from the pool. If it is not available for the
+ // current layout yet, load all resources related to the layout to the pools.
+ ImageData id = pool.get(layoutName);
+
+ if (id == null)
+ {
+
+ addImagesToPools(layoutName);
+ id = pool.get(layoutName);
+ }
+
+ return id;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getKeyDataCollection(java.lang.String)
+ */
+ public Collection<IAndroidKey> getKeyDataCollection(String layoutName)
+ {
+ Collection<IAndroidKey> androidKeys;
+ try
+ {
+ if (layoutFile == null)
+ {
+ error("User has tried to request skin data without setting a valid skin files path");
+ throw new SkinException(EmulatorNLS.ERR_AndroidSkin_NoLayoutLoaded);
+ }
+
+ // Tries to get data from the pool. If it is not available for the
+ // current layout yet, load all resources related to the layout to the pools.
+ androidKeys = androidKeysPool.get(layoutName);
+
+ if (androidKeys == null)
+ {
+
+ System.gc();
+ androidKeys =
+ AndroidSkinTranslator.generateAndroidKeys(layoutFile, layoutName,
+ skinFilesPath);
+ System.gc();
+ androidKeysPool.put(layoutName, androidKeys);
+ }
+ }
+ catch (SkinException e)
+ {
+ androidKeys = new HashSet<IAndroidKey>();
+ error("The key data could not be retrieved from skin files. Cause: " + e.getMessage());
+ EclipseUtils.showErrorDialog(e);
+ }
+
+ return androidKeys;
+ }
+
+ public Properties getKeyCodes()
+ {
+ if ((keycodes == null) || ((keycodes != null) && keycodes.isEmpty()))
+ {
+ try
+ {
+ keycodes = AndroidSkinTranslator.getKeycodes(layoutFile, skinFilesPath);
+ }
+ catch (SkinException e)
+ {
+ keycodes = new Properties();
+ error("The key data could not be retrieved from skin files. Cause: "
+ + e.getMessage());
+ }
+ }
+
+ return keycodes;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getPressedImageData(java.lang.String)
+ */
+ public ImageData getPressedImageData(String layoutName) throws SkinException
+ {
+ return getImageFromPool(layoutName, pressedImagesPool);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getEnterImageData(java.lang.String)
+ */
+ public ImageData getEnterImageData(String layoutName) throws SkinException
+ {
+ return getImageFromPool(layoutName, enterImagesPool);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getReleasedImageData(java.lang.String)
+ */
+ public ImageData getReleasedImageData(String layoutName) throws SkinException
+ {
+ return getImageFromPool(layoutName, releasedImagesPool);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getSkinBean(java.lang.String)
+ */
+ public AndroidSkinBean getSkinBean(String layoutName) throws SkinException
+ {
+ if (layoutFile == null)
+ {
+ error("User has tried to request additional skin data without setting a valid skin files path");
+ throw new SkinException(EmulatorNLS.ERR_AndroidSkin_NoLayoutLoaded);
+ }
+
+ AndroidSkinBean bean = new AndroidSkinBean();
+
+ // Fills the skin bean with information related to the part chosen for display. This bean must have
+ // at least display positioning information and scale.
+ String partName = layoutFile.getMainPartName(layoutName);
+ Point dPosition =
+ AndroidSkinTranslator.translateDisplayPosition(layoutFile, layoutName, partName,
+ skinFilesPath);
+ int dWidth = layoutFile.getDisplayWidth(partName);
+ int dHeight = layoutFile.getDisplayHeight(partName);
+
+ int dw, dh;
+ if (layoutFile.isSwapWidthHeightNeededAtLayout(layoutName))
+ {
+ dw = dHeight;
+ dh = dWidth;
+ }
+ else
+ {
+ dw = dWidth;
+ dh = dHeight;
+ }
+
+ bean.addSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_X, dPosition.x);
+ bean.addSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_Y, dPosition.y);
+ bean.addSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH, dw);
+ bean.addSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT, dh);
+ bean.addSkinPropertyValue(ISkinKeyXmlTags.SKIN_EMBEDDED_VIEW_SCALE, 10);
+
+ return bean;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#isFlipSupported()
+ */
+ public boolean isFlipSupported()
+ {
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#setSkinFilesPath(java.lang.String)
+ */
+ public void setSkinFilesPath(String skinFilesPath) throws SkinException
+ {
+
+ this.skinFilesPath = new File(skinFilesPath);
+ if (this.skinFilesPath.isDirectory())
+ {
+
+ layoutFile = LayoutFileParser.readLayout(this.skinFilesPath);
+
+ }
+ else
+ {
+ error("Provided skin files path is not a directory. Setting the skin files path operation has failed.");
+ throw new SkinException(EmulatorNLS.ERR_AndroidSkin_ProvidedSkinPathIsNotADirectory);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#isRotatedLayout(java.lang.String)
+ */
+ public boolean isSwapWidthHeightNeededAtLayout(String layoutName)
+ {
+ return layoutFile.isSwapWidthHeightNeededAtLayout(layoutName);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getAvailableLayouts()
+ */
+ public Collection<String> getAvailableLayouts()
+ {
+ return layoutFile.getLayoutNames();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getLayoutScreenCommand(java.lang.String)
+ */
+ public String getLayoutScreenCommand(String layoutName)
+ {
+ return layoutFile.getLayoutSwitchCommand(layoutName);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#nextLayout(java.lang.String)
+ */
+ public String getNextLayout(String referenceLayout)
+ {
+ info("Calculating the next layout");
+ String next = null;
+ if (referenceLayout != null)
+ {
+ List<String> layoutsList = new ArrayList<String>(getAvailableLayouts());
+
+ // Switches to the next layout if the skin supports it. The if/else clause
+ // implements a circular list
+ if (layoutsList.size() > 1)
+ {
+ int currentLayoutNum = layoutsList.indexOf(referenceLayout);
+ if (currentLayoutNum != layoutsList.size() - 1)
+ {
+ next = layoutsList.get(++currentLayoutNum);
+ }
+ else
+ {
+ next = layoutsList.get(0);
+ }
+ info("Next layout: " + next);
+ }
+ else
+ {
+ warn("The skin doesn't have multiple layouts. The operation was not performed");
+ }
+ }
+ else
+ {
+ warn("The skin doesn't have multiple layouts. The operation was not performed");
+ }
+
+ return next;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#previousLayout(java.lang.String)
+ */
+ public String getPreviousLayout(String referenceLayout)
+ {
+ info("Calculating the previous layout");
+ String previous = null;
+ if (referenceLayout != null)
+ {
+ List<String> layoutsList = new ArrayList<String>(getAvailableLayouts());
+
+ // Switches to the previous layout if the skin supports it. The if/else clause
+ // implements a circular list
+ if (layoutsList.size() > 1)
+ {
+ int currentLayoutNum = layoutsList.indexOf(referenceLayout);
+
+ if (currentLayoutNum != 0)
+ {
+ previous = layoutsList.get(--currentLayoutNum);
+ }
+ else
+ {
+ previous = layoutsList.get(layoutsList.size() - 1);
+ }
+ info("Previous layout: " + previous);
+ }
+ else
+ {
+ warn("The skin doesn't have multiple layouts. The operation was not performed");
+ }
+ }
+ else
+ {
+ warn("The skin doesn't have multiple layouts. The operation was not performed");
+ }
+
+ return previous;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getBackgroundColor(java.lang.String)
+ */
+ public RGB getBackgroundColor(String layoutName)
+ {
+ return layoutFile.getLayoutColor(layoutName, skinFilesPath);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getDpadRotation(java.lang.String)
+ */
+ public int getDpadRotation(String layoutName)
+ {
+ return layoutFile.getDpadRotation(layoutName);
+ }
+
+ /**
+ * Generates images/keys for the current layout (or main part, if a layout is not available)
+ * and store them at the appropriate pools
+ *
+ * @param key The key to be used to store data at the pools
+ *
+ * @throws SkinException If the layout file cannot be loaded
+ */
+ private void addImagesToPools(String key) throws SkinException
+ {
+ // Validates the provided string before proceeding
+ if ((key == null) || (!layoutFile.getLayoutNames().contains(key)))
+ {
+ throw new SkinException(EmulatorNLS.ERR_AndroidSkin_InvalidLayoutProvided);
+ }
+
+ System.gc();
+
+ ImageData[] images =
+ AndroidSkinTranslator.generateLayoutImages(layoutFile, key, skinFilesPath);
+
+ info("Adding released/pressed/hover images to the pool");
+ releasedImagesPool.put(key, images[0]);
+ pressedImagesPool.put(key, images[1]);
+ enterImagesPool.put(key, images[2]);
+
+ System.gc();
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/AndroidSkinTranslator.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/AndroidSkinTranslator.java
new file mode 100644
index 0000000..9e0ee89
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/AndroidSkinTranslator.java
@@ -0,0 +1,1370 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.skin.android;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Properties;
+
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Shell;
+
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.skin.AndroidPressKey;
+import com.motorola.studio.android.emulator.core.skin.IAndroidKey;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.skin.android.parser.LayoutFileModel;
+
+/**
+ * DESCRIPTION:
+ * Utility class for translating Google skin files data into objects
+ * suitable to the viewer. This includes:
+ * - Generating image data objects in memory that represent the exact images that
+ * will be drawn using SWT Image/GC/Transform graphic objects. It is necessary to
+ * create new image data objects from scratch because the operations performed by
+ * Image/GC/Transform affects only the display, keeping the associated image data
+ * objects intact. It would also be very expensive to generate those images at
+ * screen, after every mouse event (including mouse moves, which are frequent)
+ * - Generating a key data collection suitable for a given layout. The key data
+ * objects must contain positioning information that depends on the position of
+ * elements inside the layout. It is this class job to discover what are the
+ * coordinates to set at the keys
+ * - Translate the display position for a given layout. As with the key data
+ * collection, the coordinates of the display depends on the layout description
+ * and must be calculated.
+ *
+ * RESPONSIBILITY:
+ * Provide translation functionalities to convert Google format to a format
+ * suitable to the viewer
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by AndroidSkin class only
+ *
+ *
+ *
+ * This class uses several concepts, that are described below:
+ *
+ * LAYOUT FILE MODEL: A representation of the file "layout", saved as part
+ * of the skin in the skin folder. Attributes such as keyboard charmap and
+ * network are global to a layout file
+ * LAYOUT: A collection of parts, with some part integration features, such
+ * as part positioning and part rotation. Parts can be rotated independently
+ * in a layout.
+ * PART: The minimum skin view. It contains buttons and background image.
+ * A display is optional
+ * OFFSET: If a part is declared to be drawn in a layout using negative coordinates,
+ * or if a button is declared to be drawn in a part using negative coordinates,
+ * the calculated layout/part offset is different from 0 in either (x, y) directions.
+ * If such situation happen, it is mandatory to generate a bigger image in memory
+ * to have the layout/part drawn.
+ * EXPANDED PART IMAGE: Is how is called the image generated in memory due to part
+ * offset being different from zero. Layouts always have images generated in memory,
+ * but parts can be represented as a simple file load if no button demands an offset
+ * different from (0, 0)
+ *
+ * Considerations about the differences between Google format and viewer format:
+ *
+ * a) Google format supports negative coordinates for parts/buttons. To generate images
+ * that are renderable by SWT, the viewer module must translate the parts/buttons so
+ * that they fit in a single image data
+ * b) Viewer demands a pair of pressed, released and enter images and a set of IAndroidKeys.
+ * Google provides several images to use as filter when a button is pressed. To increase
+ * the performance, all the images/keys are generated during instance start time, being
+ * reused afterwards
+ * c) In Google format, the part position at the layout is ALWAYS set to the upper-left
+ * corner of the PART, not the layout. Therefore, depending on the rotation parameter
+ * we need to calculate what is the position to draw the part relative to the layout
+ *
+ * ---------------------------
+ * | | 1) OLX / OLY : Offset due to layout, if one or more part
+ * | ------------------ | position coordinates are less than 0
+ * | | ------------ | | 2) OPX / OPY : Offset due to part, if one of more button
+ * | | | | | | position coordinates are less than 0
+ * | |OPX| | | | 3) LAYOUT : Layout image area
+ * | | ----- | | | 4) PART : Part expanded image area
+ * | | |BTN| | | | 5) BACKGR : Original part background image, which demanded
+ * | | | | | | | expansion due to BTN
+ * | | ----- | | | 6) BTN : A button with negative x coordinate related to
+ * | | | BACKGR | | | BACKGR
+ * | | ------------ | |
+ * | | PART | |
+ * | OLX ------------------ |
+ * | LAYOUT |
+ * ---------------------------
+ *
+ */
+public class AndroidSkinTranslator
+{
+ /**
+ * Constant that describes a QWERTY charmap in the layout
+ */
+ private static final String CHARMAP_QWERTY = "qwerty";
+
+ /**
+ * Constant that describes a QWERTY2 charmap in the layout
+ */
+ private static final String CHARMAP_QWERTY2 = "qwerty2";
+
+ /**
+ * Path to the QWERTY codes inside the plugin
+ */
+ private static final String CHARMAP_QWERTY_FILE = "res/qwerty.properties";
+
+ /**
+ * Constant that describes a AVRCP charmap in the layout
+ */
+ private static final String CHARMAP_AVRCP = "AVRCP";
+
+ /**
+ * Path to the AVRCP codes inside the plugin
+ */
+ private static final String CHARMAP_AVRCP_FILE = "res/AVRCP.properties";
+
+ private static final String CHARMAP_OPHONE_QWERTY_FILE = "res/ophone_qwerty.properties";
+
+ private static final String CHARMAP_OPHONE_AVRCP_FILE = "res/ophone_AVRCP.properties";
+
+ /**
+ * d-pad keys
+ */
+ private static final String DPAD_DOWN = "DPAD_DOWN";
+
+ private static final String DPAD_UP = "DPAD_UP";
+
+ private static final String DPAD_LEFT = "DPAD_LEFT";
+
+ private static final String DPAD_RIGHT = "DPAD_RIGHT";
+
+ /**
+ * Translates the button information inside the layout file model into
+ * viewer compatible IAndroidKeys
+ * <br><br>
+ * @param layoutFile The model that represent the layout file
+ * @param layoutName The name of the layout where to look for buttons in the model,
+ * or <code>null</code> if no layout is available
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * <br><br>
+ * @return A collection of IAndroidKeys, describing the keys, codes and positions
+ * of the buttons in the skin
+ * <br><br>
+ * @throws SkinException If an error that prevents the translation happens, such as
+ * errors when opening required files or charmap not supported
+ */
+ static Collection<IAndroidKey> generateAndroidKeys(LayoutFileModel layoutFile,
+ String layoutName, File skinFilesPath) throws SkinException
+ {
+
+ // Retrieve a map of keycodes related to the keyboard declared at the layout
+ Collection<IAndroidKey> keyCollection = new LinkedHashSet<IAndroidKey>();
+
+ Properties keycodes = getKeycodes(layoutFile, skinFilesPath);
+
+ // Retrieve the names of the parts that compose the layout
+ Collection<String> partNames = layoutFile.getLayoutPartNames(layoutName);
+
+ // Gets the layout offset for position adjustments
+ Point offsetL = getLayoutOffset(layoutFile, layoutName, skinFilesPath);
+
+ // Iterates on the parts, looking for their buttons
+
+ for (String partName : partNames)
+ {
+
+ Point offsetP = getPartOffset(layoutFile, partName);
+ Point partSize = getPartImageSize(layoutFile, partName, skinFilesPath, offsetP);
+
+ // Iterates on the buttons of the part, creating an IAndroidKey for each in the end
+ Collection<String> buttonNames = layoutFile.getButtonNames(partName);
+
+ for (String buttonName : buttonNames)
+ {
+ String k = buttonName.toUpperCase().replace("-", "_");
+ String keyCodeStr = (String) keycodes.get(k);
+
+ // The IAndroidKey will only be generated if a keycode with the same name is found
+ if (keyCodeStr != null)
+ {
+
+ // Retrieve the button parameters needed for the key generation
+ // - The button position must be translated due to the layout and eventual offsets
+ // - The button w/h must be interpreted according to the rotation parameter. If the
+ // rotation is odd (landscape), the button width must be the key height and vice-versa.
+ // Otherwise (even, portrait), we can use the button w/h at the keys as is.
+ int buttonW = layoutFile.getButtonWidth(partName, buttonName, skinFilesPath);
+ int buttonH = layoutFile.getButtonHeight(partName, buttonName, skinFilesPath);
+ Point buttonPos =
+ translateButtonPosition(layoutFile, layoutName, partName, buttonName,
+ skinFilesPath, offsetP, partSize);
+
+ int startX = offsetL.x + buttonPos.x;
+ int startY = offsetL.y + buttonPos.y;
+ int endX, endY;
+
+ if (layoutFile.isSwapWidthHeightNeededAtLayout(layoutName, partName))
+ {
+ endX = startX + buttonH;
+ endY = startY + buttonW;
+ }
+ else
+ {
+ endX = startX + buttonW;
+ endY = startY + buttonH;
+ }
+
+ int dpadRotation = layoutFile.getDpadRotation(layoutName);
+
+ keyCodeStr = getRotatedKeyCode(k, keyCodeStr, dpadRotation, keycodes);
+
+ AndroidPressKey key =
+ new AndroidPressKey(buttonName, keyCodeStr, buttonName, startX, startY,
+ endX, endY, "", 0);
+
+ keyCollection.add(key);
+
+ }
+ }
+
+ }
+
+ return keyCollection;
+ }
+
+ /**
+ * @param keyCodeStr
+ * @param keyCodeStr2
+ * @param dpadRotation
+ * @param keycodes
+ * @return
+ */
+ private static String getRotatedKeyCode(String keyName, String keyCodeStr, int dpadRotation,
+ Properties keycodes)
+ {
+ String keyCode = keyCodeStr;
+ switch (dpadRotation % 4)
+ {
+ case 1:
+ if (DPAD_DOWN.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_RIGHT);
+ }
+ else if (DPAD_LEFT.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_DOWN);
+ }
+ else if (DPAD_RIGHT.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_UP);
+ }
+ else if (DPAD_UP.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_LEFT);
+ }
+ break;
+ case 2:
+ if (DPAD_DOWN.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_UP);
+ }
+ else if (DPAD_LEFT.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_RIGHT);
+ }
+ else if (DPAD_RIGHT.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_LEFT);
+ }
+ else if (DPAD_UP.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_DOWN);
+ }
+ break;
+ case 3:
+ if (DPAD_DOWN.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_LEFT);
+ }
+ else if (DPAD_LEFT.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_UP);
+ }
+ else if (DPAD_RIGHT.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_DOWN);
+ }
+ else if (DPAD_UP.equals(keyName))
+ {
+ keyCode = (String) keycodes.get(DPAD_RIGHT);
+ }
+ break;
+ default:
+ //Does nothing, no rotation needed.
+ break;
+ }
+ return keyCode;
+ }
+
+
+ /**
+ * Merge two ImageData objects and return the merge.
+ * <br><br>
+ * @param srcData The source ImageData
+ * @param dstData The destination ImageData
+ * @param dstX The x position in dstData where srcData will be merged
+ * @param dstY The y position in dstData where srcData will be merged
+ * <br><br>
+ * @return An array containing released and pressed image data at the positions 0 and 1, respectively
+ */
+ static ImageData mergeImageGC(ImageData srcData, ImageData dstData, int dstX, int dstY) {
+
+ Shell s = new Shell();
+
+ Image srcImg = new Image(s.getDisplay(), srcData);
+ Image dstImg = new Image(s.getDisplay(), dstData);
+
+ GC gc = new GC(dstImg);
+ gc.drawImage(srcImg, dstX, dstY);
+ gc.dispose();
+
+ return dstImg.getImageData();
+
+ }
+
+ /**
+ * Creates the layout images for released and pressed buttons
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The name of the layout where to look for images in the model
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * <br><br>
+ * @return An array containing released and pressed image data at the positions 0 and 1, respectively
+ */
+ static ImageData[] generateLayoutImages(LayoutFileModel layoutFile, String layoutName,
+ File skinFilesPath)
+ {
+
+ ImageData[] layoutImgs = new ImageData[3];
+
+ // Gets the layout offset for position adjustments
+ Point offsetL = getLayoutOffset(layoutFile, layoutName, skinFilesPath);
+
+ // Iterates on the names of the parts that compose the layout
+ Collection<String> partNames = layoutFile.getLayoutPartNames(layoutName);
+
+ for (String partName : partNames)
+ {
+
+ if (!layoutFile.partHasBg(partName))
+ {
+ continue;
+ }
+
+ if (partName.equals("portrait") || partName.equals("landscape"))
+ {
+ if (!partName.equals(layoutName))
+ {
+ continue;
+ }
+ }
+
+ // Gets the part offset for position adjustments and images loading decision
+ Point offsetP = getPartOffset(layoutFile, partName);
+ Point partSize = getPartImageSize(layoutFile, partName, skinFilesPath, offsetP);
+ int bgH = layoutFile.getBackgroundHeight(partName, skinFilesPath);
+ int bgW = layoutFile.getBackgroundWidth(partName, skinFilesPath);
+
+ // Loads the part images for released and pressed. If there is a part offset, it is needed
+ // to generated an expanded part image data
+ ImageData[] bgDatas = new ImageData[3];
+ if ((offsetP.x > 0) || (offsetP.y > 0) || (partSize.x > offsetP.x + bgW)
+ || (partSize.y > offsetP.y + bgH))
+ {
+ bgDatas[0] =
+ generateExpandedPartImageData(layoutFile, layoutName, partName, offsetP,
+ skinFilesPath, offsetP, partSize);
+ }
+ else
+ {
+ bgDatas[0] = getImageData(layoutFile, partName, null, skinFilesPath);
+ }
+
+ bgDatas[1] =
+ generateMergedWithButtonsImage(layoutFile, (ImageData) bgDatas[0].clone(),
+ partName, skinFilesPath, offsetP, false);
+
+ bgDatas[2] =
+ generateMergedWithButtonsImage(layoutFile, (ImageData) bgDatas[0].clone(),
+ partName, skinFilesPath, offsetP, true);
+
+ // Loop for generating layout images based on the part images above
+ for (int img = 0; img < 3; img++)
+ {
+ // A layout image is created only if it wasn't yet, no matter how many parts the layout has.
+ if (layoutImgs[img] == null)
+ {
+ Point layoutSize =
+ getLayoutImageSize(layoutFile, layoutName, skinFilesPath, offsetL);
+ layoutImgs[img] =
+ generateImageDataWithBackground(layoutFile, layoutName, layoutSize.x,
+ layoutSize.y, bgDatas[img], skinFilesPath);
+
+ }
+
+ // Copy the part image pixels into the layout image
+ // - Rotation units rotates the image in CLOCKWISE direction
+ // - The part position must be translated because:
+ // a) At Google layout file, the (x,y) position represents the upper left corner of the
+ // part image, no matter what is the rotation value
+ // b) We are generating a layout image referenced at the upper left corner of the layout
+ // image, and we must know where we must place the part image in terms of the layout reference
+ // - The part is rotated before merged to the layout image
+ int rotation = layoutFile.getPartRotationAtLayout(layoutName, partName);
+ Point partPos =
+ translatePartPosition(layoutFile, layoutName, partName, skinFilesPath,
+ offsetP, partSize);
+ bgDatas[img] = generateRotatedImage(bgDatas[img], rotation);
+
+ int startX = offsetL.x - offsetP.x + partPos.x;
+ int startY = offsetL.y - offsetP.y + partPos.y;
+ ImageData merge = mergeImageGC(bgDatas[img], layoutImgs[img], startX, startY);
+ layoutImgs[img] = merge;
+
+
+ }
+ }
+
+ return layoutImgs;
+ }
+
+
+
+ /**
+ * Translates the display information from the layout file into an Point referenced at the
+ * upper left corner of the layout image (or part image, if layouts are not supported by the skin)
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The name of the layout being used
+ * @param partName The name of the part which contains the display
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * <br><br>
+ * @return A point referenced at the upper left corner of the layout image, where to draw the display
+ */
+ static Point translateDisplayPosition(LayoutFileModel layoutFile, String layoutName,
+ String partName, File skinFilesPath)
+ {
+ // Gets the parameters necessary for calculation
+ Point displayPos = layoutFile.getDisplayPosition(partName);
+ int displayW = layoutFile.getDisplayWidth(partName);
+ int displayH = layoutFile.getDisplayHeight(partName);
+ Point offsetP = getPartOffset(layoutFile, partName);
+ Point partSize;
+ if (!layoutFile.partHasBg(partName))
+ {
+ partSize = getPartImageSize(layoutFile, layoutName, skinFilesPath, offsetP);
+ }
+ else
+ {
+ partSize = getPartImageSize(layoutFile, partName, skinFilesPath, offsetP);
+ }
+
+ // Update the display position, considering part offset/size and rotation
+ displayPos =
+ translatePartElementPosition(layoutFile, layoutName, partName, skinFilesPath,
+ displayPos, displayW, displayH, offsetP, partSize);
+
+ // Adjusts the position (according to the layout offset) and returns to the caller
+ Point offsetL = getLayoutOffset(layoutFile, layoutName, skinFilesPath);
+ displayPos.x += offsetL.x;
+ displayPos.y += offsetL.y;
+
+ return displayPos;
+ }
+
+ /**
+ * Retrieves the keycodes to be used at the keys.
+ * Uses as parameter the keyboard charmap declared at the layout file model
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * <br><br>
+ * @return A map of properties containing the keycodes for each key of the charmap
+ * declared at the layout file
+ * <br><br>
+ * @throws SkinException If the keycode file cannot be loaded
+ */
+ static Properties getKeycodes(LayoutFileModel layoutFile, File skinFilesPath)
+ throws SkinException
+ {
+ String charmap = layoutFile.getKeyboardCharmap();
+ Properties keycodes = new Properties();
+ InputStream is = null;
+ URL url = null;
+ try
+ {
+ // If nothing is specified, use the QWERTY charmap
+ // If it is specified QWERTY or QWERTY2, use QWERTY charmap too
+ if ((charmap == null) || (charmap.equals(CHARMAP_QWERTY))
+ || (charmap.equals(CHARMAP_QWERTY2)))
+ {
+
+ url =
+ EmulatorPlugin
+ .getDefault()
+ .getBundle()
+ .getResource(
+ SdkUtils.isOphoneSDK() ? CHARMAP_OPHONE_QWERTY_FILE
+ : CHARMAP_QWERTY_FILE);
+ }
+ else if (charmap.equals(CHARMAP_AVRCP))
+ {
+ url =
+ EmulatorPlugin
+ .getDefault()
+ .getBundle()
+ .getResource(
+ SdkUtils.isOphoneSDK() ? CHARMAP_OPHONE_AVRCP_FILE
+ : CHARMAP_AVRCP_FILE);
+ }
+ else
+ {
+ warn("The skin at " + skinFilesPath.getAbsolutePath()
+ + " does not use a supported charmap");
+ return keycodes;
+ }
+
+ is = url.openStream();
+ keycodes.load(is);
+ }
+ catch (IOException e)
+ {
+ error("There was an error reading the file " + url);
+ throw new SkinException(EmulatorNLS.ERR_AndroidSkinTranslator_ErrorReadingKeycodeFile);
+ }
+ finally
+ {
+ try
+ {
+ is.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close input stream: ", e.getMessage()); //$NON-NLS-1$
+ }
+ }
+
+ return keycodes;
+ }
+
+ public static Properties getQwertyKeyMap()
+ {
+ Properties keycodes = new Properties();
+ URL url =
+ EmulatorPlugin
+ .getDefault()
+ .getBundle()
+ .getResource(
+ SdkUtils.isOphoneSDK() ? CHARMAP_OPHONE_QWERTY_FILE
+ : CHARMAP_QWERTY_FILE);
+
+ InputStream in;
+ try
+ {
+ in = url.openStream();
+ keycodes.load(in);
+ }
+ catch (IOException e)
+ {
+ error("There was an error reading the file " + url);
+ }
+
+ return keycodes;
+
+ }
+
+ /**
+ * Calculates and retrieves the layout offset.
+ * The offset is different from (0, 0) if any part has negative coordinates (x or y) according to the
+ * specification of the layout file
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The layout that we wish to have the offset calculated
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * <br><br>
+ * @return The layout offset
+ */
+ private static Point getLayoutOffset(LayoutFileModel layoutFile, String layoutName,
+ File skinFilesPath)
+ {
+ int minX = 0;
+ int minY = 0;
+
+ Collection<String> partNames = layoutFile.getLayoutPartNames(layoutName);
+
+ for (String partName : partNames)
+ {
+ if (partName.equals("portrait") || partName.equals("landscape"))
+ {
+ if (!partName.equals(layoutName))
+ {
+ continue;
+ }
+ }
+
+ Point offsetP = getPartOffset(layoutFile, partName);
+
+ Point partSize;
+ if (layoutFile.partHasBg(partName))
+ {
+ partSize = getPartImageSize(layoutFile, partName, skinFilesPath, offsetP);
+ }
+ else
+ {
+ continue;
+ // partSize = getPartImageSize(layoutFile, layoutName, skinFilesPath, offsetP);
+ }
+
+ // The part position needs translation because its coordinates may change due to
+ // the buttons position (if the part offset is different from (0, 0))
+ Point partPos =
+ translatePartPosition(layoutFile, layoutName, partName, skinFilesPath, offsetP,
+ partSize);
+ if (partPos.x < minX)
+ {
+ minX = partPos.x;
+ }
+ if (partPos.y < minY)
+ {
+ minY = partPos.y;
+ }
+ }
+
+ Point layoutOffset = new Point(Math.abs(minX), Math.abs(minY));
+
+ return layoutOffset;
+ }
+
+ /**
+ * Calculates and retrieves a part offset.
+ * The offset is different from (0, 0) if any button that belong to the part has negative coordinates
+ * (x or y) according to the specification of the layout file
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param partName The part that we wish to have the offset calculated
+ * <br><br>
+ * @return The part offset
+ */
+ private static Point getPartOffset(LayoutFileModel layoutFile, String partName)
+ {
+ int minX = 0;
+ int minY = 0;
+
+ Collection<String> buttonNames = layoutFile.getButtonNames(partName);
+
+ for (String buttonName : buttonNames)
+ {
+ Point buttonPos = layoutFile.getButtonPosition(partName, buttonName);
+ if (buttonPos.x < minX)
+ {
+ minX = buttonPos.x;
+ }
+ if (buttonPos.y < minY)
+ {
+ minY = buttonPos.y;
+ }
+ }
+
+ Point offset = new Point(Math.abs(minX), Math.abs(minY));
+
+ return offset;
+ }
+
+ /**
+ * Retrieves the size of the layout image.
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The name of the layout to have its size calculated
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * <br><br>
+ * @return The size of the layout image
+ */
+ private static Point getLayoutImageSize(LayoutFileModel layoutFile, String layoutName,
+ File skinFilesPath, Point layoutOffset)
+ {
+ int maxX = 0;
+ int maxY = 0;
+
+ // Iterates on the layout parts, looking for the area required by each of them
+
+ Collection<String> partNames = layoutFile.getLayoutPartNames(layoutName);
+ for (String partName : partNames)
+ {
+
+ if (partName.equals("portrait") || partName.equals("landscape"))
+ {
+ if (!partName.equals(layoutName))
+ {
+ continue;
+ }
+ }
+
+ // The part position must be translated because:
+ // - At Google layout file, the part (x,y) position represents the upper left corner of the
+ // PART image, no matter what is the rotation value
+ // - We are generating a layout image referenced at the upper left corner of the LAYOUT
+ // image, and we must know where we must place the part image in terms of the layout reference
+ Point offsetP = getPartOffset(layoutFile, partName);
+ Point partSize = getPartImageSize(layoutFile, partName, skinFilesPath, offsetP);
+ Point partPos =
+ translatePartPosition(layoutFile, layoutName, partName, skinFilesPath, offsetP,
+ partSize);
+ if (layoutFile.isSwapWidthHeightNeededAtLayout(layoutName, partName))
+ {
+ // If the part is in landscape direction, we sum the width at y and
+ // height at x
+ //
+ // --------------
+ // Y | width |
+ // | |
+ // A | h | --------------------------
+ // X | e | | height |
+ // I | i | | w |
+ // S | g | | i |
+ // | h | | d |
+ // | t | | t |
+ // | | | h ROTATED PART |
+ // | PART | --------------------------
+ // --------------
+ // X AXIS
+ //
+ maxX = Math.max(maxX, layoutOffset.x + partPos.x + partSize.y);
+ maxY = Math.max(maxY, layoutOffset.y + partPos.y + partSize.x);
+ }
+ else
+ {
+ // If the part is in portrait direction, we sum the w/h as is
+ maxX = Math.max(maxX, layoutOffset.x + partPos.x + partSize.x);
+ maxY = Math.max(maxY, layoutOffset.y + partPos.y + partSize.y);
+ }
+ }
+
+ Point imgSize = new Point(maxX, maxY);
+
+ return imgSize;
+ }
+
+ /**
+ * Retrieves the minimum size of the part image.
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param partName The name of the part to have its size calculated
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * <br><br>
+ * @return The minimum size of the part image
+ */
+ private static Point getPartImageSize(LayoutFileModel layoutFile, String partName,
+ File skinFilesPath, Point partOffset)
+ {
+ // The initial maximum is set to be the width/height of the original image + part offset
+ //
+ // It will be the final part size IF there are no buttons with negative coordinates AND
+ // there are no buttons that is positioned in a coordinate close to the edge enough for not
+ // fitting (considering their width/height)
+ int bgWidth = layoutFile.getBackgroundWidth(partName, skinFilesPath);
+ int bgHeight = layoutFile.getBackgroundHeight(partName, skinFilesPath);
+ Point bgPos = layoutFile.getBackgroundPosition(partName);
+ int maxX = partOffset.x + bgPos.x + bgWidth;
+ int maxY = partOffset.y + bgPos.y + bgHeight;
+
+ // Iterates on the buttons, looking for the area required by each of them
+ Collection<String> buttonNames = layoutFile.getButtonNames(partName);
+
+ for (String buttonName : buttonNames)
+ {
+ int btWidth = layoutFile.getButtonWidth(partName, buttonName, skinFilesPath);
+ int btHeight = layoutFile.getButtonHeight(partName, buttonName, skinFilesPath);
+ Point buttonPos = layoutFile.getButtonPosition(partName, buttonName);
+
+ // If a button is described to be drawn outside the current maximum
+ // (x,y) position, update the (x,y) to make it fit
+ //
+ // -------------- --------------
+ // (x,y)| | | |
+ // --- | | |
+ // | | button | | (x,y) |
+ // --- with | | ----
+ // | negative | | | | button positioned in coordinates
+ // | coordinate| | | | inside the part, but with width
+ // | | | ---- large enough not to fit
+ // | | | |
+ // | | | |
+ // | PART | | PART |
+ // -------------- --------------
+ //
+ //
+ maxX = Math.max(maxX, partOffset.x + buttonPos.x + btWidth);
+ maxY = Math.max(maxY, partOffset.y + buttonPos.y + btHeight);
+ }
+
+ Point imgSize = new Point(maxX, maxY);
+
+ return imgSize;
+ }
+
+ /**
+ * Utility method for loading a image, given the model and part/button
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param partName The name of the part to have its background image loaded, or that contains the button
+ * @param buttonName The name of the button we wish the image loaded, or <code>null</code> if we aim to
+ * load the part background image
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * <br><br>
+ * @return An image data object, containing the image pixels
+ */
+ private static ImageData getImageData(LayoutFileModel layoutFile, String partName,
+ String buttonName, File skinFilesPath)
+ {
+ File f;
+ if (buttonName == null)
+ {
+ f = layoutFile.getBackgroundImage(partName, skinFilesPath);
+ }
+ else
+ {
+ f = new File(skinFilesPath, layoutFile.getButtonImage(partName, buttonName).getName());
+ }
+
+ return new ImageData(f.getAbsolutePath());
+ }
+
+ /**
+ * Creates a new expanded part image that contains enough space for drawing all the buttons
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The name of the layout containing the part, if there is one, or <code>null</code>
+ * @param partName The part to be expanded
+ * @param offset The part offset to use when positioning the part image at the expanded image
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * <br><br>
+ * @return An image data containing the part image with more space at the background area
+ */
+ private static ImageData generateExpandedPartImageData(LayoutFileModel layoutFile,
+ String layoutName, String partName, Point offset, File skinFilesPath, Point partOffset,
+ Point partSize)
+ {
+ ImageData img = null;
+ if (partName != null)
+ {
+ // Creates an image data with dimensions defined by partSize, and background color, depth
+ // and palette defined by bgImg
+ ImageData bgImg = getImageData(layoutFile, partName, null, skinFilesPath);
+ img =
+ generateImageDataWithBackground(layoutFile, layoutName, partSize.x, partSize.y,
+ bgImg, skinFilesPath);
+
+ // Merges the bgImg pixels at the image data
+ int[] row = new int[bgImg.width];
+ for (int i = 0; i < bgImg.height; i++)
+ {
+ bgImg.getPixels(0, i, bgImg.width, row, 0);
+ img.setPixels(offset.x, offset.y + i, bgImg.width, row, 0);
+ }
+
+ }
+
+ return img;
+ }
+
+ /**
+ * Creates an image data of dimensions x, y and the same background color as srcImg
+ * <br><br>
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The name of the layout that may have a background color defined
+ * @param width The width of the image
+ * @param height The height of the image
+ * @param srcImg An image that we wish to have its depth, palette (and perhaps background color)
+ * copied to the new image
+ * <br><br>
+ * @return An image of size (width, height), same depth and palette of srcImg and with filled with the
+ * background color defined by the model or srcImg
+ */
+ private static ImageData generateImageDataWithBackground(LayoutFileModel layoutFile,
+ String layoutName, int width, int height, ImageData srcImg, File skinFilesPath)
+ {
+ ImageData img = new ImageData(width, height, srcImg.depth, srcImg.palette);
+
+ // Discover what is the background color. This is needed to fill the
+ // spaces around the layout image
+ RGB bgColor = layoutFile.getLayoutColor(layoutName, skinFilesPath);
+ int bgPixel = srcImg.palette.getPixel(bgColor);
+
+ // Set the background color to the entire layout image
+ for (int i = 0; i < img.width; i++)
+ {
+ for (int j = 0; j < img.height; j++)
+ {
+ img.setPixel(i, j, bgPixel);
+ }
+ }
+
+ return img;
+ }
+
+ /**
+ * Creates a new image, based on imageToRotate, rotated according to rotation
+ * <br><br>
+ * @param imageToRotate The image that is used as source for rotation
+ * @param rotation (rotation * 90) results in how many degrees to rotate CLOCKWISE
+ * <br><br>
+ * @return The rotated image
+ */
+ private static ImageData generateRotatedImage(ImageData imageToRotate, int rotation)
+ {
+ // For each rotation case, generates an appropriate image data (with dimensions w/h or h/w
+ // depending on whether it is landscape or portrait) and copies the imageToRotate pixels at the
+ // appropriate positions
+ ImageData rotated;
+ switch (rotation % 4)
+ {
+ case 1:
+ // 0------- 0 j h
+ // | --- | ---------
+ // j| | | | | --- |
+ // | --- | | | | |
+ // | | | --- |
+ // h------- ---------
+ rotated =
+ new ImageData(imageToRotate.height, imageToRotate.width,
+ imageToRotate.depth, imageToRotate.palette);
+ for (int i = 0; i < imageToRotate.width; i++)
+ {
+ for (int j = 0; j < imageToRotate.height; j++)
+ {
+ rotated.setPixel(imageToRotate.height - j - 1, i,
+ imageToRotate.getPixel(i, j));
+ }
+ }
+ break;
+ case 2:
+ // 0 i w 0 i w
+ // 0------- 0-------
+ // | --- | | |
+ // j| | | | j| --- |
+ // | --- | | | | |
+ // | | | --- |
+ // h------- h-------
+ rotated =
+ new ImageData(imageToRotate.width, imageToRotate.height,
+ imageToRotate.depth, imageToRotate.palette);
+ for (int i = 0; i < imageToRotate.width; i++)
+ {
+ for (int j = 0; j < imageToRotate.height; j++)
+ {
+ rotated.setPixel(imageToRotate.width - i - 1, imageToRotate.height - j - 1,
+ imageToRotate.getPixel(i, j));
+ }
+ }
+ break;
+ case 3:
+ // 0 i w
+ // 0------- 0 j h
+ // | --- | 0---------
+ // j| | | | | --- |
+ // | --- | i| | | |
+ // | | | --- |
+ // h------- w---------
+ rotated =
+ new ImageData(imageToRotate.height, imageToRotate.width,
+ imageToRotate.depth, imageToRotate.palette);
+ for (int i = 0; i < imageToRotate.width; i++)
+ {
+ for (int j = 0; j < imageToRotate.height; j++)
+ {
+ rotated.setPixel(j, imageToRotate.width - i - 1,
+ imageToRotate.getPixel(i, j));
+ }
+ }
+ break;
+ default:
+ // If 0, there is no need to rotate
+ rotated = imageToRotate;
+ break;
+ }
+
+ return rotated;
+ }
+
+ /**
+ * Creates an image that contains buttons with proper transparency. The transparency is defined by
+ * the isEnter parameter
+ *
+ * @param layoutFile The model that represents the layout file
+ * @param baseImage The image to use as base for generation. Must be a copy, because it will be
+ * changed in-place.
+ * @param partName The name of the part where to look for buttons in the model
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * @param partOffset What is the calculated offset for the given part
+ * @param isEnter Whether the image being created will be used for enter or pressed
+ * @return
+ */
+ private static ImageData generateMergedWithButtonsImage(LayoutFileModel layoutFile,
+ ImageData baseImage, String partName, File skinFilesPath, Point partOffset,
+ boolean isEnter)
+ {
+ // Iterate on the buttons, merging the buttons pixels to the base image
+ Collection<String> buttonNames = layoutFile.getButtonNames(partName);
+ for (String buttonName : buttonNames)
+ {
+ ImageData buttonID = getImageData(layoutFile, partName, buttonName, skinFilesPath);
+ Point buttonPos = layoutFile.getButtonPosition(partName, buttonName);
+ buttonPos.x += partOffset.x;
+ buttonPos.y += partOffset.y;
+ mergeButtonData(baseImage, buttonID, buttonPos, isEnter);
+ }
+
+ return baseImage;
+ }
+
+ /**
+ * Merges the button data to the base image
+ * <br><br>
+ * @param baseImage The image that will be modified
+ * @param buttonImage The image that have the source pixels for the merge operation
+ * @param buttonPos Where the button is located
+ * @param isEnter Whether the image being created will be used for enter or pressed
+ */
+ private static void mergeButtonData(ImageData baseImage, ImageData buttonImage,
+ Point buttonPos, boolean isEnter)
+ {
+ // Pixel/alpha buffers
+ int[] baseImgPixels = new int[buttonImage.width];
+ int[] buttonImgPixels = new int[buttonImage.width];
+ byte[] buttonAlphas = new byte[buttonImage.width];
+ int[] intButtonAlphas = new int[buttonImage.width];
+
+ // For each pixel row, get the button pixel data, apply the transparency
+ // defined by alpha and copy data to the base image
+ for (int i = 0; i < buttonImage.height; i++)
+ {
+ baseImage.getPixels(buttonPos.x, buttonPos.y + i, buttonImage.width, baseImgPixels, 0);
+ buttonImage.getPixels(0, i, buttonImage.width, buttonImgPixels, 0);
+ buttonImage.getAlphas(0, i, buttonImage.width, buttonAlphas, 0);
+
+ for (int j = 0; j < buttonAlphas.length; j++)
+ {
+ // As buttonAlphas is a signed byte array with range -127 to 128, and alpha is
+ // an integer in the range 0 to 255, overflows can happen. This calculation assures
+ // that the alpha variable has correct value in an integer array.
+ intButtonAlphas[j] =
+ (buttonAlphas[j] >= 0 ? buttonAlphas[j]
+ : ((buttonAlphas[j]) & ((byte) 0x7F)) + 128);
+ }
+
+ if (!isEnter)
+ {
+ for (int j = 0; j < buttonAlphas.length; j++)
+ {
+ if (intButtonAlphas[j] > 0)
+ {
+ intButtonAlphas[j] += (255 - intButtonAlphas[j]) / 4;
+ }
+ }
+ }
+
+ addTransparency(baseImgPixels, buttonImgPixels, intButtonAlphas, baseImage.palette,
+ buttonImage.palette);
+
+ baseImage.setPixels(buttonPos.x, buttonPos.y + i, buttonImage.width, baseImgPixels, 0);
+ }
+ }
+
+ /**
+ * Calculates transparency for the button pixels and sets them to the base
+ * pixels buffer
+ * <br><br>
+ * @param basePixels The buffer containing pixels for a given line of the base image
+ * @param buttonPixels The buffer containing pixels for a given line of the button image
+ * @param buttonAlphas The buffer containing alpha information for a given line of the button image
+ * @param basePalette The color palette used by the base image
+ * @param buttonPalette The color palette used by the button image
+ */
+ private static void addTransparency(int[] basePixels, int[] buttonPixels, int[] buttonAlphas,
+ PaletteData basePalette, PaletteData buttonPalette)
+ {
+ for (int i = 0; i < buttonPixels.length; i++)
+ {
+ RGB buttonRgb = buttonPalette.getRGB(buttonPixels[i]);
+ RGB baseRgb = basePalette.getRGB(basePixels[i]);
+
+ RGB newRgb =
+ new RGB(calculateMerge(baseRgb.red, buttonRgb.red, buttonAlphas[i]),
+ calculateMerge(baseRgb.green, buttonRgb.green, buttonAlphas[i]),
+ calculateMerge(baseRgb.blue, buttonRgb.blue, buttonAlphas[i]));
+
+ basePixels[i] = basePalette.getPixel(newRgb);
+ }
+ }
+
+ /**
+ * Calculates the transparency for a single color component
+ * <br><br>
+ * @param background The background color component
+ * @param foreground The foreground color component
+ * @param alpha The alpha to be applied. 0 means pure transparent
+ * (background color is used). 255 means pure opaque (foreground color is used)
+ * <br><br>
+ * @return The resulting color
+ */
+ private static int calculateMerge(int background, int foreground, int alpha)
+ {
+ // weighted medium of foreground color and background color, with alpha as parameter
+ return (foreground * alpha + background * (255 - alpha)) / 255;
+ }
+
+ /**
+ * Translates the part position information from the Google format to the upper-left reference used
+ * by the viewer
+ *
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The layout where the part is included
+ * @param partName The part to have its position calculated
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ *
+ * @return The point where the part must be drawn in the layout, using as reference the upper-left
+ * corner of the layout image
+ */
+ private static Point translatePartPosition(LayoutFileModel layoutFile, String layoutName,
+ String partName, File skinFilesPath, Point partOffset, Point partSize)
+ {
+ // Collect needed data
+ int rotation = layoutFile.getPartRotationAtLayout(layoutName, partName);
+ Point partPos = layoutFile.getPartPositionAtLayout(layoutName, partName, skinFilesPath);
+ int bgWidth;
+ int bgHeight;
+ if (layoutFile.partHasBg(partName))
+ {
+ bgWidth = layoutFile.getBackgroundWidth(partName, skinFilesPath);
+ bgHeight = layoutFile.getBackgroundHeight(partName, skinFilesPath);
+ }
+ else
+ {
+ bgWidth = layoutFile.getBackgroundWidth(layoutName, skinFilesPath);
+ bgHeight = layoutFile.getBackgroundHeight(layoutName, skinFilesPath);
+ }
+ int extraOnEndW = partSize.x - bgWidth - partOffset.x;
+ int extraOnEndH = partSize.y - bgHeight - partOffset.y;
+
+ // Calculate translation
+ switch (rotation % 4)
+ {
+ case 1:
+ // Landscape, top of part image is at the right (90 degrees clockwise rotation)
+ // The point we must return is the one at the bottom-left corner of the part, considering
+ // offset and extra space in the end of the part image (which was added so that buttons
+ // at the right side of the part fit)
+ //
+ // BEFORE AFTER
+ // (0,0) (0,0)
+ // --------- ---------
+ // | --- | | --- |
+ // | | | | | | | |
+ // | --- | | --- |
+ // --------- ---------
+ partPos.x = partPos.x - partOffset.y - bgHeight;
+ partPos.y = partPos.y - extraOnEndW;
+ break;
+ case 2:
+ // Portrait, top of part image is at the bottom (180 degrees clockwise rotation)
+ // The point we must return is the one at the bottom-right corner of the part
+ //
+ // BEFORE AFTER
+ // (0,0)
+ // ------- -------
+ // | | | |
+ // | --- | | --- |
+ // | | | | | | | |
+ // | --- | | --- |
+ // ------- -------
+ // (0,0)
+ partPos.x = partPos.x - bgWidth;
+ partPos.y = partPos.y - bgHeight;
+ break;
+ case 3:
+ // Landscape, top of part image is at the left (270 degrees clockwise rotation)
+ // The point we must return is the one at the top-right corner of the part, considering
+ // offset and extra space in the end of the part image (which was added so that buttons
+ // at the right side of the part fit)
+ //
+ // BEFORE AFTER
+ // (0,0)
+ // --------- ---------
+ // | --- | | --- |
+ // | | | | | | | |
+ // | --- | | --- |
+ // --------- ---------
+ //(0,0)
+ partPos.x = partPos.x - extraOnEndH;
+ partPos.y = partPos.y - partOffset.x - bgWidth;
+ break;
+ default:
+ // No translation is needed when there is no rotation
+ break;
+ }
+
+ return partPos;
+ }
+
+ /**
+ * Translates the button position information from the Google format to the upper-left reference used
+ * by the viewer
+ *
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The layout where the part is included, or <code>null</code> if the skin does
+ * not support layout
+ * @param partName The part where the button is included
+ * @param buttonName The button to have its position calculated
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ *
+ * @return The point where the button must be drawn in the part, using as reference the upper-left
+ * corner of the part image
+ */
+ private static Point translateButtonPosition(LayoutFileModel layoutFile, String layoutName,
+ String partName, String buttonName, File skinFilesPath, Point partOffset, Point partSize)
+ {
+ // Collect button data
+ Point buttonPos = layoutFile.getButtonPosition(partName, buttonName);
+ int buttonW = layoutFile.getButtonWidth(partName, buttonName, skinFilesPath);
+ int buttonH = layoutFile.getButtonHeight(partName, buttonName, skinFilesPath);
+
+ // Update the button position, considering part offset/size and rotation
+ buttonPos =
+ translatePartElementPosition(layoutFile, layoutName, partName, skinFilesPath,
+ buttonPos, buttonW, buttonH, partOffset, partSize);
+
+ return buttonPos;
+ }
+
+ /**
+ * Translates a part element position (display/buttons) from the Google format to the upper-left
+ * reference used by the viewer
+ *
+ * @param layoutFile The model that represents the layout file
+ * @param layoutName The layout where the part is included, or <code>null</code> if the skin does
+ * not support layout
+ * @param partName The part where the element is included
+ * @param skinFilesPath The path to the skin dir, where skin files can be found
+ * @param partElementPos The position of the part element as described by layoutFile
+ * @param partElementWidth The width of the part element as described by layoutFile
+ * @param partElementHeight The height of the part element as described by layoutFile
+ *
+ * @return The point where the element must be drawn in the part, using as reference the upper-left
+ * corner of the part image
+ */
+ private static Point translatePartElementPosition(LayoutFileModel layoutFile,
+ String layoutName, String partName, File skinFilesPath, Point partElementPos,
+ int partElementWidth, int partElementHeight, Point partOffset, Point partSize)
+ {
+ Point translated = new Point(0, 0);
+ int rotation = layoutFile.getPartRotationAtLayout(layoutName, partName);
+
+ // Due to rotation, the part position will be referenced to a non-appropriate image corner.
+ // The following operation guarantees that the part position is still at the upper left corner
+ // even after rotation.
+ Point partPos =
+ translatePartPosition(layoutFile, layoutName, partName, skinFilesPath, partOffset,
+ partSize);
+
+ // Calculate position.
+ //
+ // OBS: Every time we need the part size for our the calculation, we must subtract the part offset
+ // as well. This is because during part size calculation, we have already summed the offset and we
+ // need to rework the offset due to rotation (i.e., sometimes we need to sum offset.y instead of
+ // offset.x due to rotation, and vice-versa). This is being illustrated at the lines below with
+ // parenthesis.
+ switch (rotation % 4)
+ {
+ case 1:
+ // BEFORE AFTER
+ //(0,0)
+ // --------- (0,0)
+ // | | -----------
+ // |(x,y) | (x,y) |
+ // | --- | | --- |
+ // | | | | | | | |
+ // | --- | | --- |
+ // --------- -----------
+ translated.x =
+ partPos.x - partOffset.x + (partSize.y - partOffset.y) - partElementPos.y
+ - partElementHeight;
+ translated.y = partPos.y - partOffset.y + partOffset.x + partElementPos.x;
+ break;
+ case 2:
+ // BEFORE AFTER
+ //(0,0) (0,0)
+ // --------- ---------
+ // | | (x,y) --- |
+ // |(x,y) | | | | |
+ // | --- | | --- |
+ // | | | | | |
+ // | --- | | |
+ // --------- ---------
+ translated.x =
+ partPos.x + (partSize.x - partOffset.x) - partOffset.x - partElementPos.x
+ - partElementWidth;
+ translated.y =
+ partPos.y + (partSize.y - partOffset.y) - partOffset.y - partElementPos.y
+ - partElementHeight;
+ break;
+ case 3:
+ // BEFORE AFTER
+ //(0,0)
+ // --------- (0,0)
+ // | | -----------
+ // |(x,y) | |(x,y)--- |
+ // | --- | | | | |
+ // | | | | | --- |
+ // | --- | | |
+ // --------- -----------
+ translated.x = partPos.x - partOffset.x + partOffset.y + partElementPos.y;
+ translated.y =
+ partPos.y - partOffset.y + (partSize.x - partOffset.x) - partElementPos.x
+ - partElementWidth;
+ break;
+ default:
+ translated.x = partElementPos.x + partPos.x;
+ translated.y = partElementPos.y + partPos.y;
+ break;
+ }
+
+ return translated;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ILayoutBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ILayoutBean.java
new file mode 100644
index 0000000..70e9510
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ILayoutBean.java
@@ -0,0 +1,27 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.skin.android.parser;
+
+public interface ILayoutBean
+{
+ /**
+ * Sets the key/value pair at the bean, according to the rules defined by each bean
+ *
+ * @param key The key that represents the item to be set
+ * @param value The value to be set to the item represented by key
+ */
+ void setKeyValue(String key, String value);
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ILayoutConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ILayoutConstants.java
new file mode 100644
index 0000000..98f71f3
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ILayoutConstants.java
@@ -0,0 +1,78 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.skin.android.parser;
+
+/**
+ * DESCRIPTION:
+ * This class lists all default constants contained into a layout file
+ *
+ * RESPONSIBILITY:
+ * Support on parsing of layout files
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by the LayoutFileParser class only
+ */
+interface ILayoutConstants
+{
+ String OPEN_BRACKET = "{";
+
+ String CLOSE_BRACKET = "}";
+
+ String MAIN_LEVEL_PARTS = "parts";
+
+ String MAIN_LEVEL_LAYOUTS = "layouts";
+
+ String MAIN_LEVEL_KEYBOARD = "keyboard";
+
+ String MAIN_LEVEL_NETWORK = "network";
+
+ String MAIN_LEVEL_BACKGROUND = "background";
+
+ String MAIN_LEVEL_DISPLAY = "display";
+
+ String MAIN_LEVEL_BUTTON = "button";
+
+ String PART_BUTTONS = "buttons";
+
+ String KEYBOARD_CHARMAP = "charmap";
+
+ String NETWORK_SPEED = "speed";
+
+ String NETWORK_DELAY = "delay";
+
+ String ATTR_WIDTH = "width";
+
+ String ATTR_HEIGHT = "height";
+
+ String ATTR_X = "x";
+
+ String ATTR_Y = "y";
+
+ String ATTR_IMAGE = "image";
+
+ String ATTR_NAME = "name";
+
+ String LAYOUT_COLOR = "color";
+
+ String LAYOUT_EVENT = "event";
+
+ String DPAD_ROTATION = "dpad-rotation";
+
+ String PARTREF_ROTATION = "rotation";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ImagePositionBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ImagePositionBean.java
new file mode 100644
index 0000000..d8a5c12
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/ImagePositionBean.java
@@ -0,0 +1,175 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.skin.android.parser;
+
+import java.io.File;
+
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * DESCRIPTION:
+ * This class represents a node containing image, x and y data
+ *
+ * RESPONSIBILITY:
+ * Represent image nodes of the layout file
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by the LayoutFileParser and LayoutFileModel classes only
+ */
+public class ImagePositionBean implements ILayoutConstants, ILayoutBean
+{
+ /**
+ * Name of the node
+ */
+ private String name;
+
+ /**
+ * Location of the file that contains the image
+ */
+ private File imageLocation;
+
+ /**
+ * X position where to draw the image
+ */
+ private String xPos;
+
+ /**
+ * Y position where to draw the image
+ */
+ private String yPos;
+
+ private int width = -1;
+
+ private int height = -1;
+
+ /**
+ * Constructor
+ * Creates the node and assign a name to it
+ */
+ ImagePositionBean(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * Retrieves the image location
+ *
+ * @return The image location
+ */
+ File getImageLocation()
+ {
+ return imageLocation;
+ }
+
+ /**
+ * Retrieves the X position where to draw the image
+ *
+ * @return The image X position
+ */
+ String getXPos()
+ {
+ return xPos;
+ }
+
+ /**
+ * Retrieves the Y position where to draw the image
+ *
+ * @return The image Y position
+ */
+ String getYPos()
+ {
+ return yPos;
+ }
+
+ int getWidth(File skinFilesPath)
+ {
+ if (width == -1)
+ {
+ populateWidthHeight(skinFilesPath);
+ }
+
+ return width;
+ }
+
+ int getHeight(File skinFilesPath)
+ {
+ if (width == -1)
+ {
+ populateWidthHeight(skinFilesPath);
+ }
+
+ return height;
+ }
+
+ /**
+ * Retrieves the name of this node
+ *
+ * @return The node name
+ */
+ String getName()
+ {
+ return name;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "ImagePosition: " + name;
+ }
+
+ private void populateWidthHeight(final File skinFilesPath)
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ ImageData id =
+ new ImageData(new File(skinFilesPath, imageLocation.getName())
+ .getAbsolutePath());
+ width = id.width;
+ height = id.height;
+ }
+ });
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.skin.android.parser.ILayoutBean#setKeyValue(java.lang.String, java.lang.String)
+ */
+ public void setKeyValue(String key, String value)
+ {
+ if (ATTR_IMAGE.equals(key))
+ {
+ imageLocation = new File(value);
+ }
+ else if (ATTR_X.equals(key))
+ {
+ xPos = value;
+ }
+ else if (ATTR_Y.equals(key))
+ {
+ yPos = value;
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutBean.java
new file mode 100644
index 0000000..586f10e
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutBean.java
@@ -0,0 +1,231 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.skin.android.parser;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+import org.eclipse.swt.graphics.RGB;
+
+/**
+ * DESCRIPTION:
+ * This class represents a layout structure from the layout file
+ *
+ * RESPONSIBILITY:
+ * Represent layout structures
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by the LayoutFileParser and LayoutFileModel classes only
+ */
+public class LayoutBean implements ILayoutConstants, ILayoutBean
+{
+ /**
+ * Default name of a landscape layout (used when creating pseudo-layouts)
+ * Pseudo-layouts are created when the skin has only one part and no layouts
+ */
+ public static final String DEFAULT_LAYOUT_NAME = "default";
+
+ /**
+ * Default name of a portrait layout (used when creating pseudo-layouts)
+ * Pseudo-layouts are created when the skin has only one part and no layouts
+ */
+ public static final String ROTATED_LAYOUT_NAME = "rotated";
+
+ /**
+ * The layout structure name
+ */
+ private String name;
+
+ /**
+ * The layout width
+ */
+ private String width;
+
+ /**
+ * The layout height
+ */
+ private String height;
+
+ /**
+ * Thw layout color
+ */
+ private RGB color;
+
+ /**
+ * The event command used to switch to this layout
+ */
+ private String event;
+
+ /**
+ * Dpad rotation, steps of 90°
+ */
+ private int dpadRotation;
+
+ /**
+ * The collection of parts that integrate this layout
+ */
+ private Collection<PartRefBean> parts = new LinkedHashSet<PartRefBean>();
+
+ /**
+ * Constructor
+ * Builds a new layout structure with the given name
+ *
+ * @param name The layout name
+ */
+ LayoutBean(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * Creates a new reference to a part, registers it and returns it to the user
+ *
+ * @param refName The name to assign to the part reference
+ *
+ * @return The part reference
+ */
+ PartRefBean newPartRef(String refName)
+ {
+ PartRefBean bean = new PartRefBean(refName);
+ parts.add(bean);
+ return bean;
+ }
+
+ /**
+ * Retrieves the layout width
+ *
+ * @return The layout width
+ */
+ String getWidth()
+ {
+ return width;
+ }
+
+ /**
+ * Retrieves the layout height
+ *
+ * @return The layout height
+ */
+ String getHeight()
+ {
+ return height;
+ }
+
+ /**
+ * Retrieves the layout color
+ *
+ * @return The layout color
+ */
+ RGB getColor()
+ {
+ return color;
+ }
+
+ /**
+ * Retrieves the event to switch to this layout
+ *
+ * @param event The event
+ */
+ String getEvent()
+ {
+ return event;
+ }
+
+ /**
+ * @return the dpadRotation
+ */
+ int getDpadRotation()
+ {
+ return dpadRotation;
+ }
+
+ /**
+ * Retrieves this layout name
+ *
+ * @return This layout name
+ */
+ String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Retrieves all references to parts from this layout
+ *
+ * @return A collection containing part references
+ */
+ Collection<PartRefBean> getPartRefs()
+ {
+ return parts;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "Layout: " + name;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.skin.android.parser.ILayoutBean#setKeyValue(java.lang.String, java.lang.String)
+ */
+ public void setKeyValue(String key, String value)
+ {
+ if (ATTR_WIDTH.equals(key))
+ {
+ width = value;
+ }
+ else if (ATTR_HEIGHT.equals(key))
+ {
+ height = value;
+ }
+ else if (LAYOUT_COLOR.equals(key))
+ {
+ Integer colorInt = Integer.decode(value);
+ int blue = colorInt.intValue() & 0xFF;
+ int green = (colorInt.intValue() & 0xFF00) >> 8;
+ int red = (colorInt.intValue() & 0xFF0000) >> 16;
+ RGB rgb = new RGB(red, green, blue);
+
+ color = rgb;
+ }
+ else if (LAYOUT_EVENT.equals(key))
+ {
+ event = value;
+ }
+ else if (DPAD_ROTATION.equals(key))
+ {
+ int intValue;
+ try
+ {
+ intValue = Integer.parseInt(value);
+ }
+ catch (NumberFormatException e)
+ {
+ //Assume there's no rotation
+ intValue = 0;
+ }
+ dpadRotation = intValue;
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutFileModel.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutFileModel.java
new file mode 100644
index 0000000..922b396
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutFileModel.java
@@ -0,0 +1,706 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.skin.android.parser;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+
+/**
+ * DESCRIPTION:
+ * This class represents a skin layout file
+ *
+ * RESPONSIBILITY:
+ * Represent all the contents of a skin layout file
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use the class public APIs to retrieve data of the skin
+ */
+public class LayoutFileModel
+{
+ /**
+ * Event to rotate the screen
+ */
+ private static final String ROTATE_SCREEN_EVENT = "EV_SW:0:0";
+
+ /**
+ * Command to rotate the screen
+ */
+ private static final String ROTATE_SCREEN_CMD = "5 0 0";
+
+ /**
+ * Command to get the screen back to its default orientation
+ */
+ private static final String RETURN_TO_DEFAULT_SCREEN_CMD = "5 0 1";
+
+ /**
+ * A collection containing all layouts read from the layout file
+ */
+ private Collection<LayoutBean> layouts;
+
+ /**
+ * A collection containing all parts read from the layout file
+ */
+ private Collection<PartBean> parts = new LinkedHashSet<PartBean>();
+
+ /**
+ * The keyboard charmap used by this skin
+ */
+ private String keyboardCharmap;
+
+ /**
+ * The default network speed used by this skin
+ */
+ private String networkSpeed;
+
+ /**
+ * The default network delay used by this skin
+ */
+ private String networkDelay;
+
+ /**
+ * Sets the keyboard charmap used by this skin
+ *
+ * @param keyboardCharmap The keyboard charmap name
+ */
+ void setKeyboardCharmap(String keyboardCharmap)
+ {
+ this.keyboardCharmap = keyboardCharmap;
+ }
+
+ /**
+ * Sets the default network speed used by this skin
+ *
+ * @param keyboardCharmap The default network speed used by this skin
+ */
+ void setNetworkSpeed(String networkSpeed)
+ {
+ this.networkSpeed = networkSpeed;
+ }
+
+ /**
+ * Sets the default network delay of this skin
+ *
+ * @param The default network delay
+ */
+ void setNetworkDelay(String networkDelay)
+ {
+ this.networkDelay = networkDelay;
+ }
+
+ /**
+ * Creates a new part, registers it and returns it to the user
+ *
+ * This version is used when the skin is simple, i.e. when there is a single
+ * part and no layouts defined. To support landscape/portrait rotation, we create
+ * "pseudo-layouts" at memory as well.
+ *
+ * @return The part
+ */
+ PartBean newPart()
+ {
+ return newPart(PartBean.UNIQUE_PART);
+ }
+
+ /**
+ * Creates a new part, registers it and returns it to the user
+ * Use this version when the skin have multiple parts
+ * (i.e., if there is a "parts" element in the layout file)
+ *
+ * @param name The part name
+ *
+ * @return The part
+ */
+ PartBean newPart(String name)
+ {
+ PartBean bean = new PartBean(name);
+ parts.add(bean);
+ return bean;
+ }
+
+ /**
+ * Creates a new layout, registers it and returns it to the user
+ *
+ * @param name The layout name
+ *
+ * @return The layout
+ */
+ LayoutBean newLayout(String name)
+ {
+ LayoutBean bean = new LayoutBean(name);
+ if (layouts == null)
+ {
+ layouts = new LinkedHashSet<LayoutBean>();
+ }
+ layouts.add(bean);
+
+ return bean;
+ }
+
+ /**
+ * Retrieves the keyboard charmap used by this skin
+ *
+ * @return The keyboard charmap name
+ */
+ public String getKeyboardCharmap()
+ {
+ return keyboardCharmap;
+ }
+
+ /**
+ * Retrieves the default network speed of this skin
+ *
+ * @return The default network speed
+ */
+ public String getNetworkSpeed()
+ {
+ return networkSpeed;
+ }
+
+ /**
+ * Retrieves the default network delay of this skin
+ *
+ * @return The default network delay
+ */
+ public String getNetworkDelay()
+ {
+ return networkDelay;
+ }
+
+ public List<String> getLayoutNames()
+ {
+ List<String> layoutNames = new LinkedList<String>();
+ if (layouts != null)
+ {
+ for (LayoutBean bean : layouts)
+ {
+ layoutNames.add(bean.getName());
+ }
+ }
+
+ return layoutNames;
+ }
+
+ public Collection<String> getPartNames()
+ {
+ Collection<String> partNames = new LinkedHashSet<String>();
+ for (PartBean bean : parts)
+ {
+ partNames.add(bean.getName());
+ }
+
+ return partNames;
+ }
+
+ public Collection<String> getLayoutPartNames(String layoutName)
+ {
+ Collection<String> layoutPartNames = new LinkedHashSet<String>();
+ LayoutBean bean = getLayoutByName(layoutName);
+ if (bean != null)
+ {
+ Collection<PartRefBean> partRefs = bean.getPartRefs();
+ if (partRefs != null)
+ {
+ for (PartRefBean aRef : partRefs)
+ {
+ layoutPartNames.add(aRef.getPartName());
+ }
+ }
+ }
+
+ return layoutPartNames;
+ }
+
+ public Point getPartPositionAtLayout(String layoutName, String partName, File skinFilesPath)
+ {
+ Point partPosition = null;
+
+ LayoutBean bean = getLayoutByName(layoutName);
+ if (bean != null)
+ {
+ Collection<PartRefBean> partRefs = bean.getPartRefs();
+ for (PartRefBean prBean : partRefs)
+ {
+ if (prBean.getPartName().equals(partName))
+ {
+ int x = Integer.parseInt(prBean.getX());
+ String yStr = prBean.getY();
+ int y;
+ if (yStr == null)
+ {
+ y = getBackgroundWidth(partName, skinFilesPath);
+ }
+ else
+ {
+ y = Integer.parseInt(yStr);
+ }
+
+ partPosition = new Point(x, y);
+ break;
+ }
+ }
+ }
+ else
+ {
+ partPosition = new Point(0, 0);
+ }
+
+ return partPosition;
+ }
+
+ public int getPartRotationAtLayout(String layoutName, String partName)
+ {
+ int partRotation = 0;
+
+ LayoutBean bean = getLayoutByName(layoutName);
+ if (bean != null)
+ {
+ Collection<PartRefBean> partRefs = bean.getPartRefs();
+ for (PartRefBean prBean : partRefs)
+ {
+ if (prBean.getPartName().equals(partName))
+ {
+ String rotStr = prBean.getRotation();
+ if (rotStr != null)
+ {
+ partRotation = Integer.parseInt(rotStr);
+ }
+ break;
+ }
+ }
+ }
+
+ return partRotation;
+ }
+
+ public int getDpadRotation(String layoutName)
+ {
+ int dPadRotation = 0;
+ LayoutBean bean = getLayoutByName(layoutName);
+ if (bean != null)
+ {
+ dPadRotation = bean.getDpadRotation();
+ }
+
+ return dPadRotation;
+ }
+
+ public Collection<String> getButtonNames(String partName)
+ {
+ Collection<String> buttonNames = new LinkedHashSet<String>();
+
+ PartBean bean = getPartByName(partName);
+ if (bean != null)
+ {
+ Collection<ImagePositionBean> buttons = bean.getButtons();
+ if (buttons != null)
+ {
+ for (ImagePositionBean button : buttons)
+ {
+ buttonNames.add(button.getName());
+ }
+ }
+ }
+ return buttonNames;
+ }
+
+ public int getLayoutWidth(String layoutName)
+ {
+ int width = 0;
+ LayoutBean layout = getLayoutByName(layoutName);
+ if (layout != null)
+ {
+ width = Integer.parseInt(layout.getWidth());
+ }
+
+ return width;
+ }
+
+ public int getLayoutHeight(String layoutName)
+ {
+ int height = 0;
+ LayoutBean layout = getLayoutByName(layoutName);
+ if (layout != null)
+ {
+ height = Integer.parseInt(layout.getHeight());
+ }
+
+ return height;
+ }
+
+ public RGB getLayoutColor(String layoutName, File skinFilesPath)
+ {
+ RGB color = null;
+ LayoutBean layout = getLayoutByName(layoutName);
+ if (layout != null)
+ {
+ color = layout.getColor();
+ if (color == null)
+ {
+ String mainPart = getMainPartName(layoutName);
+ File image = getBackgroundImage(mainPart, skinFilesPath);
+ ImageData img = new ImageData(image.getAbsolutePath());
+ int pixel = img.getPixel(0, 0);
+ color = img.palette.getRGB(pixel);
+ layout.setKeyValue(ILayoutConstants.LAYOUT_COLOR,
+ "0x" + Integer.toHexString(color.red) + Integer.toHexString(color.green)
+ + Integer.toHexString(color.blue));
+ }
+ }
+ else
+ {
+ color = new RGB(255, 255, 255);
+ }
+
+ return color;
+ }
+
+ public String getLayoutEvent(String layoutName)
+ {
+ String event = "";
+ LayoutBean layout = getLayoutByName(layoutName);
+ if (layout != null)
+ {
+ event = layout.getEvent();
+ }
+
+ return event;
+ }
+
+ public String getLayoutSwitchCommand(String layoutName)
+ {
+ LayoutBean bean = getLayoutByName(layoutName);
+ String event = bean.getEvent();
+ if (ROTATE_SCREEN_EVENT.equals(event))
+ {
+ return ROTATE_SCREEN_CMD;
+ }
+ else
+ {
+ return RETURN_TO_DEFAULT_SCREEN_CMD;
+ }
+ }
+
+ public boolean isSwapWidthHeightNeededAtLayout(String layoutName)
+ {
+ return isSwapWidthHeightNeededAtLayout(layoutName, getMainPartName(layoutName));
+ }
+
+ public boolean isSwapWidthHeightNeededAtLayout(String layoutName, String partName)
+ {
+ boolean isRotated = false;
+ if (partName != null)
+ {
+ int rotation = getPartRotationAtLayout(layoutName, partName);
+ isRotated = rotation % 2 != 0;
+ }
+ return isRotated;
+ }
+
+ public File getBackgroundImage(String partName, File skinFilesPath)
+ {
+ File backgroundFile = null;
+ PartBean part = getPartByName(partName);
+ if (part != null)
+ {
+ ImagePositionBean backgroundBean = part.getBackground();
+ if (backgroundBean != null)
+ {
+ backgroundFile =
+ new File(skinFilesPath, backgroundBean.getImageLocation().getName());
+ }
+ }
+
+ return backgroundFile;
+ }
+
+ public Point getBackgroundPosition(String partName)
+ {
+ Point bgPosition = null;
+ PartBean part = getPartByName(partName);
+ if (part != null)
+ {
+ ImagePositionBean bgBean = part.getBackground();
+ String xStr = null;
+ String yStr = null;
+ if (bgBean != null)
+ {
+
+ xStr = bgBean.getXPos();
+ yStr = bgBean.getYPos();
+ }
+
+ if ((xStr != null) && (yStr != null))
+ {
+ bgPosition = new Point(Integer.parseInt(xStr), Integer.parseInt(yStr));
+ }
+ else
+ {
+ bgPosition = new Point(0, 0);
+ }
+ }
+
+ return bgPosition;
+ }
+
+ public int getBackgroundWidth(String partName, File skinFilesPath)
+ {
+ int width = -1;
+ PartBean part = getPartByName(partName);
+ if (part != null)
+ {
+ ImagePositionBean bgBean = part.getBackground();
+ if (bgBean != null)
+ {
+ width = bgBean.getWidth(skinFilesPath);
+ }
+ }
+
+ return width;
+ }
+
+ public int getBackgroundHeight(String partName, File skinFilesPath)
+ {
+ int height = -1;
+ PartBean part = getPartByName(partName);
+ if (part != null)
+ {
+ ImagePositionBean bgBean = part.getBackground();
+ if (bgBean != null)
+ {
+ height = bgBean.getHeight(skinFilesPath);
+ }
+ }
+
+ return height;
+ }
+
+ public File getButtonImage(String partName, String buttonName)
+ {
+ File buttonFile = null;
+ ImagePositionBean button = getButtonByName(partName, buttonName);
+ if (button != null)
+ {
+ buttonFile = button.getImageLocation();
+ }
+
+ return buttonFile;
+ }
+
+ public Point getButtonPosition(String partName, String buttonName)
+ {
+ Point buttonPos = null;
+ ImagePositionBean button = getButtonByName(partName, buttonName);
+ if (button != null)
+ {
+ buttonPos =
+ new Point(Integer.parseInt(button.getXPos()),
+ Integer.parseInt(button.getYPos()));
+ }
+
+ return buttonPos;
+ }
+
+ public int getButtonWidth(String partName, String buttonName, File skinFilesPath)
+ {
+ int width = -1;
+ ImagePositionBean button = getButtonByName(partName, buttonName);
+ if (button != null)
+ {
+ width = button.getWidth(skinFilesPath);
+ }
+
+ return width;
+ }
+
+ public int getButtonHeight(String partName, String buttonName, File skinFilesPath)
+ {
+ int height = -1;
+ ImagePositionBean button = getButtonByName(partName, buttonName);
+ if (button != null)
+ {
+ height = button.getHeight(skinFilesPath);
+ }
+
+ return height;
+ }
+
+ public Point getDisplayPosition(String partName)
+ {
+ Point displayPos = null;
+
+ PartBean bean = getPartByName(partName);
+ if (bean != null)
+ {
+ RectangleBean dispBean = bean.getDisplay();
+ displayPos =
+ new Point(Integer.parseInt(dispBean.getXPos()), Integer.parseInt(dispBean
+ .getYPos()));
+ }
+
+ return displayPos;
+ }
+
+ public int getDisplayWidth(String partName)
+ {
+ int width = -1;
+
+ PartBean bean = getPartByName(partName);
+ if (bean != null)
+ {
+ RectangleBean dispBean = bean.getDisplay();
+ width = Integer.parseInt(dispBean.getWidth());
+ }
+
+ return width;
+ }
+
+ public int getDisplayHeight(String partName)
+ {
+ int height = -1;
+
+ PartBean bean = getPartByName(partName);
+ if (bean != null)
+ {
+ RectangleBean dispBean = bean.getDisplay();
+ height = Integer.parseInt(dispBean.getHeight());
+ }
+
+ return height;
+ }
+
+ private LayoutBean getLayoutByName(String layoutName)
+ {
+ LayoutBean layoutToReturn = null;
+
+ if (layouts != null)
+ {
+ Iterator<LayoutBean> it = layouts.iterator();
+ while (it.hasNext())
+ {
+ LayoutBean bean = it.next();
+ if (bean.getName().equals(layoutName))
+ {
+ layoutToReturn = bean;
+ break;
+ }
+ }
+ }
+
+ return layoutToReturn;
+ }
+
+ public PartBean getPartByName(String partName)
+ {
+ PartBean partToReturn = null;
+
+ Iterator<PartBean> it = parts.iterator();
+ while (it.hasNext())
+ {
+ PartBean bean = it.next();
+ if (bean.getName().equals(partName))
+ {
+ partToReturn = bean;
+ break;
+ }
+ }
+
+ return partToReturn;
+ }
+
+ private ImagePositionBean getButtonByName(String partName, String buttonName)
+ {
+ ImagePositionBean buttonToReturn = null;
+ PartBean pBean = getPartByName(partName);
+ if (pBean != null)
+ {
+ Collection<ImagePositionBean> buttons = pBean.getButtons();
+ if (buttons != null)
+ {
+ for (ImagePositionBean bBean : buttons)
+ {
+ if (bBean.getName().equals(buttonName))
+ {
+ buttonToReturn = bBean;
+ break;
+ }
+ }
+ }
+ }
+
+ return buttonToReturn;
+ }
+
+ /**
+ * Retrieves a layout main part, i.e. the first part that contains a display
+ * In future releases, check if it is needed to change this concept of "main part"
+ *
+ * @param layoutName The layout which main part is to be discovered
+ *
+ * @return The name of the layout main part
+ */
+ public String getMainPartName(String layoutName)
+ {
+ String mainPartName = null;
+ for (LayoutBean layout : layouts)
+ {
+ if (layout.getName().equals(layoutName))
+ {
+ Collection<PartRefBean> allRefs = layout.getPartRefs();
+ for (PartRefBean partRef : allRefs)
+ {
+ String partName = partRef.getPartName();
+ for (PartBean aPart : parts)
+ {
+ String aPartName = aPart.getName();
+ if ((aPartName.equals(partName)) && (aPart.getDisplay() != null))
+ {
+ mainPartName = aPartName;
+ break;
+ }
+ }
+ if (mainPartName != null)
+ {
+ break;
+ }
+ }
+ }
+ if (mainPartName != null)
+ {
+ break;
+ }
+ }
+
+ return mainPartName;
+ }
+
+ public boolean partHasBg(String partName)
+ {
+ PartBean part = getPartByName(partName);
+ ImagePositionBean background = part.getBackground();
+ return background != null;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutFileParser.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutFileParser.java
new file mode 100644
index 0000000..92d7664
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/LayoutFileParser.java
@@ -0,0 +1,552 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.skin.android.parser;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.net.URL;
+import java.nio.CharBuffer;
+import java.util.Collection;
+import java.util.EmptyStackException;
+import java.util.Stack;
+
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+
+/**
+ * DESCRIPTION:
+ * This class parses a layout file into a LayoutFileModel object
+ *
+ * RESPONSIBILITY:
+ * Parse the layout file
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Call readLayout method passing a file to be parsed and retrieve the
+ * correspondent LayoutFileModel object
+ */
+public class LayoutFileParser implements ILayoutConstants
+{
+ /**
+ * Name of the layout descriptor file at the skin folder
+ */
+ private static final String LAYOUT_FILE_NAME = "layout";
+
+ /**
+ * Name of the pseudo layout descriptor file at the res folder
+ */
+ private static final String PSEUDO_LAYOUT_FILE = "res/pseudolayout";
+
+ /**
+ * The pattern used for generating tokens out of the layout file
+ */
+ private static final String SPLIT_PATTERN = "[\n\r \t]+";
+
+ /**
+ * Parses a layout file
+ *
+ * @param skinFilesPath The path to the skin folder
+ *
+ * @return a model containing all data read from the layout file
+ *
+ * @throws SkinException If it is not possible to read the layout file
+ */
+ public static LayoutFileModel readLayout(File skinFilesPath) throws SkinException
+ {
+ LayoutFileModel model = new LayoutFileModel();
+ File layoutPath = new File(skinFilesPath, LAYOUT_FILE_NAME);
+ String fileContents;
+ if ((layoutPath != null) && (layoutPath.isFile()))
+ {
+ fileContents = getLayoutFileContents(layoutPath);
+ parseLayoutFile(fileContents, model);
+ }
+
+ Collection<String> partNames = model.getPartNames();
+ if ((model.getLayoutNames().size() == 0) && (partNames.size() == 1)
+ && (partNames.iterator().next().equals(PartBean.UNIQUE_PART)))
+ {
+ fileContents = getPseudoLayoutFileContents();
+ parseLayoutFile(fileContents, model);
+ }
+
+ return model;
+ }
+
+ /**
+ * Parses the provided layout file contents
+ *
+ * @param fileContents All the contents of a layout file
+ * @param model The model where to set the parsed data
+ *
+ * @throws SkinException If the layout file is corrupted, or has erroneous syntax
+ */
+ private static void parseLayoutFile(String fileContents, LayoutFileModel model)
+ throws SkinException
+ {
+ // process given string to remove comments
+ String cleanContents = "";
+ BufferedReader reader = null;
+ try
+ {
+ StringBuffer contentBuffer = new StringBuffer();
+ reader = new BufferedReader(new StringReader(fileContents));
+ String line = null;
+ do
+ {
+ line = reader.readLine();
+ String lineCopy = line;
+ if ((line != null) && !lineCopy.trim().startsWith("#"))
+ {
+ contentBuffer.append(line + '\n');
+ }
+ }
+ while (line != null);
+ cleanContents = contentBuffer.toString();
+ }
+ catch (IOException e)
+ {
+ //try to continue with the parser
+ cleanContents = fileContents;
+ }
+ finally
+ {
+ try
+ {
+ reader.close();
+ }
+ catch (IOException e)
+ {
+ StudioLogger.error("Could not close input stream: ", e.getMessage()); //$NON-NLS-1$
+ }
+ }
+
+ String[] tokens = cleanContents.split(SPLIT_PATTERN);
+
+ Stack<Object> stack = new Stack<Object>();
+
+ // At this point, the file has been read into hundreds of token, including blocks,
+ // "{", "}", keys and values
+
+ String currentTag = null;
+ String key = null;
+
+ // Iterate on the tokens
+ try
+ {
+ for (String aToken : tokens)
+ {
+ // When the token is a "{", that means we need to stack something. This "something"
+ // will be removed from stack when we find a matching "}"
+ if (OPEN_BRACKET.equals(aToken))
+ {
+ // Every word is interpreted as a key at first. If we find a "{", it must be
+ // re-interpreted as a tag instead
+ if (key != null)
+ {
+ currentTag = key;
+ key = null;
+ }
+ addElementsToStack(stack, model, currentTag);
+ }
+ // When the token is a "}" we must remove something from the stack
+ else if (CLOSE_BRACKET.equals(aToken))
+ {
+ removeElementsFromStack(stack);
+ }
+ else
+ {
+ // A word is interpreted as a key by default. If the key is already set, we will
+ // have a key-value pair and are able to assign it to something at the model
+ if (key == null)
+ {
+ key = aToken;
+ }
+ else
+ {
+ setKeyValuePair(stack, model, currentTag, key, aToken);
+ key = null;
+ }
+ }
+ }
+
+ }
+ catch (EmptyStackException e)
+ {
+ throw new SkinException(EmulatorNLS.ERR_LayoutFileParser_BracketsDoNotMatch);
+ }
+
+ if (!stack.isEmpty())
+ {
+ // When there is only a part bean at the first level, that means we have finished
+ // parsing a single part layout. Remove it from the stack as well.
+ //
+ // NOTE: when creating the single part layout, we have added this additional element
+ // to the stack
+ if ((stack.size() == 1) && (stack.get(0) instanceof PartBean)
+ && (((PartBean) stack.get(0)).getName().equals(PartBean.UNIQUE_PART)))
+ {
+ stack.pop();
+ }
+ else
+ {
+ throw new SkinException(EmulatorNLS.ERR_LayoutFileParser_BracketsDoNotMatch);
+ }
+ }
+
+ }
+
+ /**
+ * Reads the contents of the provided file into a String object
+ *
+ * @param layoutPath A file pointing to an "layout" file
+ *
+ * @return A string with all the contents of the file
+ *
+ * @throws SkinException If the file cannot be read
+ */
+ private static String getLayoutFileContents(File layoutPath) throws SkinException
+ {
+ int fileSize = (int) layoutPath.length();
+ char[] buffer = new char[fileSize];
+
+ FileReader fr = null;
+ try
+ {
+ fr = new FileReader(layoutPath);
+ fr.read(buffer);
+ }
+ catch (IOException e)
+ {
+ error("The file " + layoutPath.getAbsolutePath() + " could not be read. cause="
+ + e.getMessage());
+ throw new SkinException(EmulatorNLS.ERR_LayoutFileParser_LayoutFileCouldNotBeRead);
+ }
+ finally
+ {
+ try
+ {
+ if (fr != null)
+ {
+ fr.close();
+ }
+ }
+ catch (IOException e)
+ {
+ warn("The file " + layoutPath.getAbsolutePath()
+ + " could not be closed after reading");
+ }
+ }
+
+ return String.copyValueOf(buffer);
+ }
+
+ /**
+ * Gets the contents of the pseudo layout file, for merging to the current model
+ *
+ * @return A string containing all the contents of the pseudo layout file
+ *
+ * @throws SkinException If the file cannot be read
+ */
+ private static String getPseudoLayoutFileContents() throws SkinException
+ {
+ URL url = EmulatorPlugin.getDefault().getBundle().getResource(PSEUDO_LAYOUT_FILE);
+ CharBuffer buffer = CharBuffer.allocate(1024);
+ int readChars = 0;
+
+ InputStream is = null;
+ InputStreamReader isr = null;
+ try
+ {
+ is = url.openStream();
+ isr = new InputStreamReader(is);
+ while (readChars != -1)
+ {
+ readChars = isr.read(buffer);
+ }
+ buffer.flip();
+ }
+ catch (IOException e)
+ {
+ error("The file res/pseudolayout could not be read. cause=" + e.getMessage());
+ throw new SkinException(EmulatorNLS.ERR_LayoutFileParser_LayoutFileCouldNotBeRead);
+ }
+ finally
+ {
+ try
+ {
+ if (is != null)
+ {
+ is.close();
+ }
+ if (isr != null)
+ {
+ isr.close();
+ }
+ }
+ catch (IOException e)
+ {
+ warn("The file " + PSEUDO_LAYOUT_FILE + " could not be closed after reading");
+ }
+ }
+
+ return buffer.toString();
+ }
+
+ /**
+ * Stacks an element
+ * The stack rules are quite complex. We first start by special cases (in which we analyze the stack
+ * and the current element for accurate interpretation) and then move to the default cases.
+ *
+ * Summarizing, the stack will contain objects from this package (*Bean) as well as String objects.
+ * When a bean is at the top of the stack, we may perform actions on the given object. Strings are added
+ * to the stack for bracket matching and to add a mark for future actions.
+ *
+ * @param stack The stack where to add elements
+ * @param model The model being built
+ * @param elementName The name of the element to add to stack.
+ */
+ private static void addElementsToStack(Stack<Object> stack, LayoutFileModel model,
+ String elementName)
+ {
+ int stackSizeAtStart = stack.size();
+
+ //--------------
+ // SPECIAL CASES
+ //--------------
+
+ // When the stack size is equal to zero, we can have one of those two situations:
+ //
+ // a) THE LAYOUT FILE CONTAINS MULTIPLE LAYOUT AND/OR PARTS: It is possible to have the
+ // following tags: "parts", "layouts", "keyboard" or "network". All of them are handled in the
+ // else clause, by adding the tag name at the stack
+ //
+ // b) THE LAYOUT FILE IS SIMPLE (i.e. it doesn't contain layouts, neither a collection
+ // of parts): It is possible to have the following tags: "display", "background", "button",
+ // "keyboard", "network". The first three belong to a part definition, so we need to include a
+ // PartBean to the stack before the object representing the tag. The last two can be handled the same
+ // way as in item (a)
+ if (stack.size() == 0)
+ {
+ if ((MAIN_LEVEL_DISPLAY.equals(elementName))
+ || (MAIN_LEVEL_BACKGROUND.equals(elementName))
+ || (MAIN_LEVEL_BUTTON.equals(elementName)))
+ {
+ // This is a single part layout. Execute operation described at item (b) above
+ PartBean bean = model.newPart();
+ stack.push(bean);
+
+ if ((MAIN_LEVEL_BACKGROUND.equals(elementName))
+ || (MAIN_LEVEL_BUTTON.equals(elementName)))
+ {
+ stack.push(elementName);
+ }
+ else if (MAIN_LEVEL_DISPLAY.equals(elementName))
+ {
+ RectangleBean display = bean.newDisplay();
+ stack.push(display);
+ }
+ }
+ else
+ {
+ // PARTS, LAYOUTS, KEYBOARD, NETWORK
+ stack.push(elementName);
+ }
+ }
+
+ // When the stack size is equal to one, we can have one of those four situations:
+ //
+ // a) THE ELEMENT AT STACK IS NOT A STRING: In this case, we will handle as default case
+ // b) THE ELEMENT AT STACK IS THE "parts" STRING: It means that the element name denotes the name of
+ // a part. We must create a part with the name of the element, and add it to the stack
+ // c) THE ELEMENT AT STACK IS THE "layouts" STRING: It means that the element name denotes the name of
+ // a layout. We must create a layout with the name of the element, and add it to the stack
+ // d) THE ELEMENT AT STACK IS ANY OTHER STRING: In this case, we will handle as default case
+ else if (stack.size() == 1)
+ {
+ Object previousElement = stack.peek();
+ if (previousElement instanceof String)
+ {
+ if (MAIN_LEVEL_PARTS.equals((String) previousElement))
+ {
+ // elementName is the name of a new part
+ PartBean bean = model.newPart(elementName);
+ stack.push(bean);
+ }
+ else if (MAIN_LEVEL_LAYOUTS.equals((String) previousElement))
+ {
+ // elementName is the name of a new layout
+ LayoutBean bean = model.newLayout(elementName);
+ stack.push(bean);
+ }
+ }
+ }
+
+ //--------------
+ // DEFAULT CASES
+ //--------------
+
+ // Any other case will be handled below. The following clauses cover any other remaining cases not
+ // covered by the special cases. The beans created, when added to the stack, represents structures
+ // already known. If it is not possible to guess what structure we need at the current parse iteration
+ // or if we need an element at the stack to match a close bracket to come, we simply add it as string
+ //
+ // We only execute the following block if the previous cases didn't affect the stack
+ if (stackSizeAtStart == stack.size())
+ {
+ Object stackElem = stack.peek();
+ if (stackElem instanceof PartBean)
+ {
+ if (MAIN_LEVEL_DISPLAY.equals(elementName))
+ {
+ RectangleBean display = ((PartBean) stackElem).newDisplay();
+ stack.push(display);
+ }
+ else if (MAIN_LEVEL_BACKGROUND.equals(elementName))
+ {
+ ImagePositionBean background =
+ ((PartBean) stackElem).newBackground(elementName);
+ stack.push(background);
+ }
+ else
+ {
+ stack.push(elementName);
+ }
+ }
+ else if (stackElem instanceof LayoutBean)
+ {
+ PartRefBean bean = ((LayoutBean) stackElem).newPartRef(elementName);
+ stack.push(bean);
+ }
+ else if (stackElem instanceof String)
+ {
+ if ((MAIN_LEVEL_BUTTON.equals((String) stackElem) || (PART_BUTTONS
+ .equals((String) stackElem))))
+ {
+ Object nonStringObj = findFirstNonStringAtStack(stack);
+ if (nonStringObj != null)
+ {
+ PartBean bean = (PartBean) nonStringObj;
+ ImagePositionBean button = bean.newButton(elementName);
+ stack.push(button);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes an element from the stack
+ *
+ * @param stack The stack from where to remove elements
+ */
+ private static void removeElementsFromStack(Stack<Object> stack)
+ {
+ stack.pop();
+ }
+
+ /**
+ * Set a key-value pair at the object at the top of the stack.
+ * Depending on the key-value pair, we may set attributes to the model itself
+ *
+ * @param stack The stack containing the element to have a property set
+ * @param model The model that can have a property set
+ * @param currentTag The name of the tag containing a model property
+ * @param key The property key
+ * @param value The property value
+ */
+ private static void setKeyValuePair(Stack<Object> stack, LayoutFileModel model,
+ String currentTag, String key, String value)
+ {
+ Object obj = stack.peek();
+ if (obj instanceof String)
+ {
+ Object notStringObj = findFirstNonStringAtStack(stack);
+
+ if (notStringObj instanceof ILayoutBean)
+ {
+ ((ILayoutBean) notStringObj).setKeyValue(key, value);
+ }
+ else
+ {
+ if (MAIN_LEVEL_NETWORK.equals(currentTag))
+ {
+ if (NETWORK_DELAY.equals(key))
+ {
+ model.setNetworkDelay(value);
+ }
+ else if (NETWORK_SPEED.equals(key))
+ {
+ model.setNetworkSpeed(value);
+ }
+ }
+ else if (MAIN_LEVEL_KEYBOARD.equals(currentTag))
+ {
+ if (KEYBOARD_CHARMAP.equals(key))
+ {
+ model.setKeyboardCharmap(value);
+ }
+ }
+ }
+ }
+ else
+ {
+ ((ILayoutBean) obj).setKeyValue(key, value);
+ }
+ }
+
+ /**
+ * Utility method for finding the first non-String object at the stack
+ *
+ * @param stack The stack were to find the first non-String at
+ *
+ * @return The non-String object
+ */
+ private static Object findFirstNonStringAtStack(Stack<Object> stack)
+ {
+ Object firstNonString = null;
+ Object tmpObj = null;
+
+ int i = stack.size() - 1;
+ while (i >= 0)
+ {
+ tmpObj = stack.get(i);
+ if (!(tmpObj instanceof String))
+ {
+ firstNonString = tmpObj;
+ break;
+ }
+ else
+ {
+ i--;
+ }
+ }
+
+ return firstNonString;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/PartBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/PartBean.java
new file mode 100644
index 0000000..3a2625f
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/PartBean.java
@@ -0,0 +1,154 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.skin.android.parser;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+/**
+ * DESCRIPTION:
+ * This class represents a part structure from the layout file
+ *
+ * RESPONSIBILITY:
+ * Represent part structures
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by the LayoutFileParser and LayoutFileModel classes only
+ */
+public class PartBean
+{
+ /**
+ * The string used as name of a part, at skins that have a single part
+ */
+ public static final String UNIQUE_PART = "___UNIQUE___";
+
+ /**
+ * The part name
+ */
+ private String name;
+
+ /**
+ * The part background data
+ */
+ private ImagePositionBean background;
+
+ /**
+ * The part display data
+ */
+ private RectangleBean display;
+
+ /**
+ * The part buttons
+ */
+ private Collection<ImagePositionBean> buttons = new LinkedHashSet<ImagePositionBean>();
+
+ /**
+ * Constructor
+ * Builds a new part structure with the given name
+ *
+ * @param name The layout name
+ */
+ PartBean(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * Creates a new button, registers it and returns it to the user
+ *
+ * @param buttonName The name to assign to the button
+ *
+ * @return The button
+ */
+ ImagePositionBean newButton(String buttonName)
+ {
+ ImagePositionBean bean = new ImagePositionBean(buttonName);
+ buttons.add(bean);
+ return bean;
+ }
+
+ /**
+ * Creates a new display and registers it
+ *
+ * @return The display
+ */
+ RectangleBean newDisplay()
+ {
+ display = new RectangleBean();
+ return display;
+ }
+
+ /**
+ * Creates a new background and registers it
+ *
+ * @param bgName The name of the background image
+ *
+ * @return The background
+ */
+ ImagePositionBean newBackground(String bgName)
+ {
+ background = new ImagePositionBean(bgName);
+ return background;
+ }
+
+ /**
+ * Retrieves the part background information
+ *
+ * @return The part background information
+ */
+ ImagePositionBean getBackground()
+ {
+ return background;
+ }
+
+ /**
+ * Retrieves the part display information
+ *
+ * @return The part display information
+ */
+ RectangleBean getDisplay()
+ {
+ return display;
+ }
+
+ /**
+ * Retrieves the part name
+ *
+ * @return The part name
+ */
+ String getName()
+ {
+ return name;
+ }
+
+ Collection<ImagePositionBean> getButtons()
+ {
+ return buttons;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "Part: " + name;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/PartRefBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/PartRefBean.java
new file mode 100644
index 0000000..72e176d
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/PartRefBean.java
@@ -0,0 +1,149 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.skin.android.parser;
+
+/**
+ * DESCRIPTION:
+ * This class represents a part reference.
+ * It links a layout with a part from the same layout file
+ *
+ * RESPONSIBILITY:
+ * Represent part references and provide link information between parts and layouts
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by the LayoutFileParser and LayoutFileModel classes only
+ */
+public class PartRefBean implements ILayoutConstants, ILayoutBean
+{
+ /**
+ * The name of this part reference
+ */
+ private String refName;
+
+ /**
+ * The name of the part that this reference points to
+ */
+ private String partName;
+
+ /**
+ * The X position of the part into the layout
+ */
+ private String x;
+
+ /**
+ * The Y position of the part into the layout
+ */
+ private String y;
+
+ /**
+ * The rotation applied to the part, when into a layout
+ */
+ private String rotation;
+
+ /**
+ * Constructor
+ * Creates a part reference with the given name
+ *
+ * @param refName The part reference name
+ */
+ PartRefBean(String refName)
+ {
+ this.refName = refName;
+ }
+
+ /**
+ * Retrieves the name of the part being referenced
+ *
+ * @return The part name
+ */
+ String getPartName()
+ {
+ return partName;
+ }
+
+ /**
+ * Retrieves the reference name
+ *
+ * @return The reference name
+ */
+ String getRefName()
+ {
+ return refName;
+ }
+
+ /**
+ * Retrieves the X position where to draw this part at the layout
+ *
+ * @return The X position
+ */
+ String getX()
+ {
+ return x;
+ }
+
+ /**
+ * Retrieves the Y position where to draw this part at the layout
+ *
+ * @return The Y position
+ */
+ String getY()
+ {
+ return y;
+ }
+
+ /**
+ * Retrieves how many rotations to perform on the part when drawing it into the layout
+ *
+ * @return How many rotations to perform on the part
+ */
+ String getRotation()
+ {
+ return rotation;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "PartRef: " + refName;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.skin.android.parser.ILayoutBean#setKeyValue(java.lang.String, java.lang.String)
+ */
+ public void setKeyValue(String key, String value)
+ {
+ if (ATTR_X.equals(key))
+ {
+ x = value;
+ }
+ else if (ATTR_Y.equals(key))
+ {
+ y = value;
+ }
+ else if (ATTR_NAME.equals(key))
+ {
+ partName = value;
+ }
+ else if (PARTREF_ROTATION.equals(key))
+ {
+ rotation = value;
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/RectangleBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/RectangleBean.java
new file mode 100644
index 0000000..0d26f7b
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/skin/android/parser/RectangleBean.java
@@ -0,0 +1,127 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.skin.android.parser;
+
+/**
+ * DESCRIPTION:
+ * This class represents a node containing x, y, width and height data
+ *
+ * RESPONSIBILITY:
+ * Represent rectangles of the layout file
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by the LayoutFileParser and LayoutFileModel classes only
+ */
+public class RectangleBean implements ILayoutConstants, ILayoutBean
+{
+ /**
+ * The X position of the rectangle origin
+ */
+ private String xPos;
+
+ /**
+ * The Y position of the rectangle origin
+ */
+ private String yPos;
+
+ /**
+ * The width of the rectangle
+ */
+ private String width;
+
+ /**
+ * The height of the rectangle
+ */
+ private String height;
+
+ /**
+ * Retrieves the X position of the rectangle origin
+ *
+ * @return the X position
+ */
+ String getXPos()
+ {
+ return xPos;
+ }
+
+ /**
+ * Retrieves the Y position of the rectangle origin
+ *
+ * @return the Y position
+ */
+ String getYPos()
+ {
+ return yPos;
+ }
+
+ /**
+ * Retrieves the width of the rectangle
+ *
+ * @return The rectangle width
+ */
+ String getWidth()
+ {
+ return width;
+ }
+
+ /**
+ * Retrieves the height of the rectangle
+ *
+ * @return The rectangle height
+ */
+ String getHeight()
+ {
+ return height;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "Rectangle";
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.skin.android.parser.ILayoutBean#setKeyValue(java.lang.String, java.lang.String)
+ */
+ public void setKeyValue(String key, String value)
+ {
+ if (ATTR_X.equals(key))
+ {
+ xPos = value;
+ }
+ else if (ATTR_Y.equals(key))
+ {
+ yPos = value;
+ }
+ else if (ATTR_WIDTH.equals(key))
+ {
+ width = value;
+ }
+ else if (ATTR_HEIGHT.equals(key))
+ {
+ height = value;
+ }
+
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/IAndroidUIConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/IAndroidUIConstants.java
new file mode 100644
index 0000000..e79f700
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/IAndroidUIConstants.java
@@ -0,0 +1,24 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui;
+
+public interface IAndroidUIConstants
+{
+ /**
+ * Screens to skin proportion
+ */
+ public static final double DISPLAY_TO_SKIN_RATIO = 0.5;
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/IUIHelpConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/IUIHelpConstants.java
new file mode 100644
index 0000000..df96437
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/IUIHelpConstants.java
@@ -0,0 +1,31 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+/**
+ * DESCRIPTION:
+ * This interface contains constants used for building ids for context help at UI classes
+ */
+public interface IUIHelpConstants
+{
+ // Android Emulator viewer constants
+
+ String EMULATOR_VIEW_HELP = EmulatorPlugin.PLUGIN_ID + ".emulator";
+
+ String EMULATOR_VIEW_MAIN_DISPLAY_HELP = EmulatorPlugin.PLUGIN_ID + ".maindisplay";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/IAndroidComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/IAndroidComposite.java
new file mode 100644
index 0000000..1df03e7
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/IAndroidComposite.java
@@ -0,0 +1,80 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.controls;
+
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+
+/**
+ * This interface defines the methods that must be implemented by the
+ * composites that holds Android Emulator instances.
+ */
+public interface IAndroidComposite
+{
+
+ /**
+ * Applies the zoom factor to the components of the composite.
+ */
+ void applyZoomFactor();
+
+ /**
+ * Gets the current zoom factor.
+ * @return the zoom factor
+ */
+ double getZoomFactor();
+
+ /**
+ * Sets the zoom factor.
+ * @param zoom the zoom factor
+ */
+ void setZoomFactor(double zoom);
+
+ /**
+ * Applies the layout to the components of the composite.
+ *
+ * @param layoutName The name of the layout to apply
+ */
+ void applyLayout(String layoutName);
+
+ /**
+ * Gets is the selected fit to window option.
+ * @return the zoom factor
+ */
+ boolean isFitToWindowSelected();
+
+ /**
+ * Retrieves the key listener to apply to the main display
+ *
+ * @return The key listener
+ */
+ KeyListener getKeyListener();
+
+ /**
+ * Retrieves the mouse listener to apply to the main display
+ *
+ * @return The mouse listener
+ */
+ MouseListener getMouseListener();
+
+ /**
+ * Retrieves the mouse move listener to apply to the main display
+ *
+ * @return The mouse move listener
+ */
+ MouseMoveListener getMouseMoveListener();
+
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/RemoteCLIDisplay.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/RemoteCLIDisplay.java
new file mode 100644
index 0000000..41e6083
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/RemoteCLIDisplay.java
@@ -0,0 +1,277 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.controls;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.ISWTPainter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+
+/**
+ * DESCRIPTION:
+ * This class implements the composite that displays the contents of the
+ * CLI display, according to the provided painter object.
+ *
+ * RESPONSIBILITY:
+ * - Display the contents of the CLI display at screen
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Add the composite to a view or other equivalent SWT object for it to
+ * display the result of the communication through VNC Protocol
+ *
+ */
+public class RemoteCLIDisplay extends Composite
+{
+ private Canvas canvas;
+
+ private Image screen = null;
+
+ private ISWTPainter painter;
+
+ private Timer refreshTimer;
+
+ private static long FIRST_REFRESH_DELAY_MS = 500; /* Time in milliseconds for the first update */
+
+ private static long REFRESH_DELAY_PERIOD_MS = 300; /* Time in milliseconds between 2 updates */
+
+ private boolean active = false;
+
+ private double zoomFactor = 1;
+
+ /**
+ * Creates a new RemoteCLIDisplay object.
+ *
+ * @param parent The parent composite
+ * @param cliPainter The object where to retrieve pixels from
+ */
+ public RemoteCLIDisplay(Composite parent, ISWTPainter cliPainter)
+ {
+ super(parent, SWT.BACKGROUND);
+ this.painter = cliPainter;
+ this.setLayout(parent.getLayout());
+ canvas = new Canvas(this, SWT.BACKGROUND);
+ }
+
+ /**
+ * Starts the display refresh
+ */
+ synchronized public void start()
+ {
+ addRefreshTimer();
+ setRunning(true);
+ }
+
+ /**
+ * Stops the display refresh
+ */
+ synchronized public void stop()
+ {
+ setRunning(false);
+ refreshTimer.cancel();
+
+ if (!canvas.isDisposed())
+ {
+ GC gc = new GC(canvas);
+ canvas.drawBackground(gc, 0, 0, canvas.getSize().x, canvas.getSize().y);
+ gc.dispose();
+ }
+ }
+
+ /**
+ * Adds a timer that schedules the screen's update in a fixed period.
+ */
+ private void addRefreshTimer()
+ {
+ refreshTimer = new Timer();
+
+ final IAndroidEmulatorInstance instance = UIHelper.getInstanceAssociatedToControl(this);
+
+ refreshTimer.scheduleAtFixedRate(new TimerTask()
+ {
+ @Override
+ public void run()
+ {
+ if (instance.getHasCli())
+ {
+ // Request CLI Update here (when applicable)
+
+ getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ updateScreen();
+ }
+ });
+ }
+ }
+ }, FIRST_REFRESH_DELAY_MS, REFRESH_DELAY_PERIOD_MS);
+ }
+
+ /**
+ * Performs the screen update itself
+ */
+ private void updateScreen()
+ {
+ if (screen != null)
+ {
+ screen.dispose();
+ }
+
+ if ((!getDisplay().isDisposed()) && (isDisplayActive()))
+ {
+ if ((painter.getImageData() != null) && (!canvas.isDisposed()))
+ {
+ screen =
+ new Image(canvas.getDisplay(), painter.getImageData().scaledTo(
+ (int) (painter.getImageData().width * zoomFactor),
+ (int) (painter.getImageData().height * zoomFactor)));
+
+ GC gc = new GC(canvas);
+ gc.drawImage(screen, 0, 0);
+ gc.dispose();
+ }
+ }
+ else
+ {
+ stop();
+ }
+ }
+
+ /**
+ * Returns true if the component is running.
+ */
+ synchronized public boolean isDisplayActive()
+ {
+ return active;
+ }
+
+ /**
+ * Gets the Canvas used to show the screen.
+ *
+ * @return the Canvas object.
+ */
+ public Canvas getCanvas()
+ {
+ return canvas;
+ }
+
+ /**
+ * Retrieves the image being drawn at display
+ *
+ * @return The image being drawn at display
+ */
+ public Image getScreen()
+ {
+ return screen;
+ }
+
+ /**
+ * Retrieves the display width
+ *
+ * @return The display width
+ */
+ public int getScreenWidth()
+ {
+ return painter.getWidth();
+ }
+
+ /**
+ * Retrieves the display height
+ *
+ * @return The display height
+ */
+ public int getScreenHeight()
+ {
+ return painter.getHeight();
+ }
+
+ /**
+ * Sets the current state of the display
+ *
+ * @param running true if the display is refreshing; false otherwise
+ */
+ synchronized private void setRunning(boolean running)
+ {
+ this.active = running;
+ }
+
+ /**
+ * @see org.eclipse.swt.widgets.Widget#dispose()
+ */
+ @Override
+ public void dispose()
+ {
+ if (isDisplayActive())
+ {
+ stop();
+ }
+
+ if (screen != null)
+ {
+ screen.dispose();
+ }
+
+ canvas.dispose();
+ super.dispose();
+ }
+
+ /**
+ * @see org.eclipse.swt.widgets.Control#setBackground(Color)
+ */
+ @Override
+ public void setBackground(Color color)
+ {
+ super.setBackground(color);
+ canvas.setBackground(color);
+ }
+
+ /**
+ * Retrieves the current zoom factor being applied to the screen
+ *
+ * @return The current zoom factor
+ */
+ public double getZoomFactor()
+ {
+ return zoomFactor;
+ }
+
+ /**
+ * Sets a new zoom factor to the screen
+ *
+ * @param zoomFactor The zoom factor to set to the screen
+ */
+ public void setZoomFactor(double zoomFactor)
+ {
+ this.zoomFactor = zoomFactor;
+
+ IAndroidEmulatorInstance instance = UIHelper.getInstanceAssociatedToControl(this);
+ if (instance.getHasCli())
+ {
+ updateScreen();
+ }
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/UIHelper.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/UIHelper.java
new file mode 100644
index 0000000..751811d
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/UIHelper.java
@@ -0,0 +1,145 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.controls;
+
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.IRemoteDisplay;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.ui.controls.maindisplay.MainDisplayComposite;
+
+/**
+ * DESCRIPTION:
+ * This class provides helper methods for Android Composite UI
+ *
+ * RESPONSIBILITY:
+ * - Provide utility methods related to Android Composite UI
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use any of the public methods for getting help with skin UI
+ */
+public class UIHelper
+{
+ /**
+ * Retrieves the instance related to a given control
+ *
+ * @param control The control associated to an instance, which is to be retrieved
+ *
+ * @return The instance associated with the control
+ */
+ public static IAndroidEmulatorInstance getInstanceAssociatedToControl(Control control)
+ {
+ IAndroidEmulatorInstance result = null;
+ Control composite = null;
+ TabFolder folder = null;
+
+ if (control instanceof ScrolledComposite)
+ {
+ composite = ((ScrolledComposite) control).getContent();
+ folder = (TabFolder) composite.getParent();
+ }
+ else if (control instanceof MainDisplayComposite)
+ {
+ composite = control.getParent();
+ folder = (TabFolder) control.getParent().getParent();
+ }
+ else if ((control instanceof Composite) && (control instanceof IAndroidComposite))
+ {
+ composite = control;
+ folder = (TabFolder) control.getParent();
+ }
+ else if ((control instanceof SWTRemoteDisplay) || (control instanceof RemoteCLIDisplay))
+ {
+ composite = control.getParent();
+ folder = (TabFolder) composite.getParent();
+ }
+
+ if (folder != null)
+ {
+ TabItem[] items = folder.getItems();
+ for (TabItem item : items)
+ {
+ if (item.getControl() == composite)
+ {
+ result = (IAndroidEmulatorInstance) item.getData();
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public static SWTRemoteDisplay getRemoteDisplayAssociatedToControl(Control control)
+ {
+
+ SWTRemoteDisplay remoteDisplay = null;
+
+ if ((control instanceof Composite) && (control instanceof IAndroidComposite))
+ {
+ for (Control childControl : ((Composite) control).getChildren())
+ {
+ if (childControl instanceof SWTRemoteDisplay)
+ {
+ remoteDisplay = (SWTRemoteDisplay) childControl;
+ break;
+ }
+ }
+ }
+
+ return remoteDisplay;
+ }
+
+ /**
+ * Ajusts the x,y coordinates of the mouse event according to the current zoom and rotation.
+ * Coordinates are originally set with the x,y coordinates of the UI element which may be resized according to zoom and rotation,
+ * but the x,y coordinates expected by the Android emulator is independent of the zoom and rotation.
+ * @param e the mouse event whose coordinates will be ajusted.
+ */
+ public static void ajustCoordinates(MouseEvent e, IAndroidComposite composite)
+ {
+ int x;
+ int y;
+
+ SWTRemoteDisplay mainDisplay =
+ UIHelper.getRemoteDisplayAssociatedToControl((Control) composite);
+ IRemoteDisplay.Rotation rotation = mainDisplay.getRotation();
+ double zoomFactor = composite.getZoomFactor();
+
+ switch (rotation)
+ {
+ case ROTATION_90DEG_COUNTERCLOCKWISE:
+ x = mainDisplay.getScreenWidth() - (int) ((double) e.y / zoomFactor);
+ y = (int) ((double) e.x / zoomFactor);
+ break;
+ default:
+ x = (int) ((double) e.x / zoomFactor);
+ y = (int) ((double) e.y / zoomFactor);
+ }
+
+ e.x = x;
+ e.y = y;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/maindisplay/MainDisplayComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/maindisplay/MainDisplayComposite.java
new file mode 100644
index 0000000..c5a4561
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/maindisplay/MainDisplayComposite.java
@@ -0,0 +1,470 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.controls.maindisplay;
+
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.Properties;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.IRemoteDisplay;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseWheelListener;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.model.IInputLogic;
+import com.motorola.studio.android.emulator.logic.AndroidLogicUtils;
+import com.motorola.studio.android.emulator.skin.android.AndroidSkinTranslator;
+import com.motorola.studio.android.emulator.ui.controls.IAndroidComposite;
+import com.motorola.studio.android.emulator.ui.controls.UIHelper;
+import com.motorola.studio.android.emulator.ui.handlers.IHandlerConstants;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * This class is the composite that holds the main display and it is shown by
+ * the Emulator Main Display View.
+ */
+public class MainDisplayComposite extends Composite implements IAndroidComposite
+{
+
+ /**
+ * The zoom factor whose default value is 1.0 (100%)
+ */
+ private double zoomFactor = 1.0;
+
+ private double fitZoomfactor;
+
+ // Minimum value to be used as zoom factor. This is necessary to avoid
+ // divisions to zero
+ private static final double MINIMUM_ZOOM_FACTOR = 0.0001;
+
+ private static final double ZOOM_FIT = 0.0;
+
+ /**
+ * The flag indicating that Ctrl key is pressed
+ */
+ private boolean ctrlPressed = false;
+
+ /**
+ * SWT key pressed/released events listener.
+ */
+ private KeyListener keyListener;
+
+ private MouseListener mouseListener;
+
+ private MouseMoveListener mouseMoveListener;
+
+ private IInputLogic androidInput;
+
+ private boolean isMouseLeftButtonPressed;
+
+ private boolean isFitToWindow;
+
+ private IAndroidEmulatorInstance androidInstance;
+
+ private Properties keyMap;
+
+ /**
+ * Constructor
+ *
+ * @param parent
+ * composite
+ * @param style
+ * style
+ * @param baseWidth
+ * the default main display width
+ * @param baseHeight
+ * the default main display height
+ */
+ public MainDisplayComposite(Composite parent, int style, int baseWidth, int baseHeight,
+ IAndroidEmulatorInstance instance)
+ {
+ super(parent, style);
+
+ androidInput = instance.getInputLogic();
+
+ androidInstance = instance;
+
+ isMouseLeftButtonPressed = false;
+
+ keyMap = AndroidSkinTranslator.getQwertyKeyMap();
+
+ addListener();
+
+ if (!Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ hideEmulatorWindow();
+ }
+
+ }
+
+ private void hideEmulatorWindow()
+ {
+ int port =
+ AndroidLogicUtils.getEmulatorPort(DDMSFacade.getSerialNumberByName(androidInstance
+ .getName()));
+ long windowHandle = NativeUIUtils.getWindowHandle(androidInstance.getName(), port);
+ androidInstance.setWindowHandle(windowHandle);
+
+ NativeUIUtils.hideWindow(windowHandle);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Widget#dispose()
+ */
+ @Override
+ public void dispose()
+ {
+ if (androidInput != null)
+ {
+ androidInput.dispose();
+ }
+
+ keyListener = null;
+ mouseListener = null;
+ mouseMoveListener = null;
+
+ if (!Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ long hnd = androidInstance.getWindowHandle();
+ if (hnd > 0)
+ {
+ NativeUIUtils.showWindow(hnd);
+ NativeUIUtils.restoreWindow(hnd);
+ }
+
+ //Force update on redrawing
+ androidInstance.setWindowHandle(0);
+ }
+
+ super.dispose();
+ }
+
+ /**
+ * Updates the composite size when zoom is changed.
+ *
+ * @param zoom
+ * the zoom factor
+ */
+ public void setZoomFactor(double zoomFactor)
+ {
+ info("Update detached view composite size");
+ if (zoomFactor == ZOOM_FIT)
+ {
+ isFitToWindow = true;
+ }
+ else
+ {
+ isFitToWindow = false;
+ }
+ this.zoomFactor = zoomFactor;
+ }
+
+ /**
+ * Gets the zoom factor.
+ *
+ * @return zoom the zoom factor.
+ */
+ public double getZoomFactor()
+ {
+ return zoomFactor;
+ }
+
+ /**
+ * Applies the zoom factor to the components of the composite, updating the
+ * composite size to hold totally the main display.
+ */
+ public void applyZoomFactor()
+ {
+
+ SWTRemoteDisplay mainDisplay = UIHelper.getRemoteDisplayAssociatedToControl(this);
+ IRemoteDisplay.Rotation rotation = mainDisplay.getRotation();
+
+ int baseHeight;
+ int baseWidth;
+
+ switch (rotation)
+ {
+ case ROTATION_90DEG_COUNTERCLOCKWISE:
+ baseHeight = mainDisplay.getScreenWidth();
+ baseWidth = mainDisplay.getScreenHeight();
+ break;
+ default:
+ baseHeight = mainDisplay.getScreenHeight();
+ baseWidth = mainDisplay.getScreenWidth();
+
+ }
+
+ int width;
+ int height;
+ if (isFitToWindow)
+ {
+ Rectangle clientArea = getParent().getClientArea();
+ if ((clientArea.width == 0) || (clientArea.height == 0))
+ {
+ // zoom factor cannot be zero, otherwise an
+ // IllegalArgumentException
+ // is raised in some SWT methods
+ fitZoomfactor = MINIMUM_ZOOM_FACTOR;
+ }
+ else
+ {
+ double widthRatio = (double) (clientArea.width) / baseWidth;
+ double heightRatio = (double) (clientArea.height) / baseHeight;
+ fitZoomfactor = Math.min(widthRatio, heightRatio);
+ }
+ width = new Double(baseWidth * fitZoomfactor).intValue();
+ height = new Double(baseHeight * fitZoomfactor).intValue();
+
+ if (mainDisplay != null)
+ {
+ mainDisplay.setZoomFactor(fitZoomfactor);
+ }
+ }
+ else
+ {
+ width = new Double(baseWidth * zoomFactor).intValue();
+ height = new Double(baseHeight * zoomFactor).intValue();
+
+ if (mainDisplay != null)
+ {
+ mainDisplay.setZoomFactor(zoomFactor);
+ }
+ }
+
+ setSize(width, height);
+ }
+
+ /**
+ * Adds listener for SWT events.
+ */
+ private void addListener()
+ {
+ // add listener to handle keyboard key pressing
+ keyListener = new KeyListener()
+ {
+
+ public void keyPressed(KeyEvent arg0)
+ {
+
+ int keyCode = arg0.keyCode;
+
+ if (keyCode == SWT.CTRL)
+ {
+ ctrlPressed = true;
+ }
+ else
+ {
+ // send message to emulator
+ androidInput.sendKey(arg0.character, keyCode, keyMap);
+ }
+
+ }
+
+ public void keyReleased(KeyEvent arg0)
+ {
+ int keyCode = arg0.keyCode;
+
+ if (keyCode == SWT.CTRL)
+ {
+ ctrlPressed = false;
+ }
+ }
+
+ };
+
+ // listener to change the zoom factor using Ctrl + Mouse Wheel
+ addMouseWheelListener(new MouseWheelListener()
+ {
+
+ public void mouseScrolled(MouseEvent event)
+ {
+ if (ctrlPressed)
+ {
+
+ if ((event.count > 0) && (zoomFactor < IHandlerConstants.MAXIMUM_ZOOM))
+ {
+ // increase zoom factor
+ setZoomFactor(zoomFactor + IHandlerConstants.STEP_ZOOM);
+ applyZoomFactor();
+ }
+
+ else if ((event.count < 0) && (zoomFactor > IHandlerConstants.MINIMUM_ZOOM))
+ {
+ // decrease zoom factor
+ setZoomFactor(zoomFactor - IHandlerConstants.STEP_ZOOM);
+ applyZoomFactor();
+ }
+ }
+ }
+ });
+
+ mouseListener = new MouseAdapter()
+ {
+ /**
+ * @see org.eclipse.swt.events.MouseListener#mouseUp(MouseEvent)
+ */
+ @Override
+ public void mouseUp(MouseEvent e)
+ {
+ handleMouseUp(e);
+ }
+
+ /**
+ * @see org.eclipse.swt.events.MouseListener#mouseDown(MouseEvent)
+ */
+ @Override
+ public void mouseDown(MouseEvent e)
+ {
+ setFocus();
+ handleMouseDown(e);
+ }
+ };
+
+ mouseMoveListener = new MouseMoveListener()
+ {
+ /**
+ * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(MouseEvent)
+ */
+ public void mouseMove(MouseEvent e)
+ {
+ handleMouseMove(e);
+ }
+ };
+
+ getParent().addControlListener(new ControlAdapter()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.ControlAdapter#controlResized(org.eclipse.swt.events.ControlEvent)
+ */
+ @Override
+ public void controlResized(ControlEvent event)
+ {
+ if (isFitToWindow)
+ {
+ applyZoomFactor();
+ }
+ }
+ });
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.ui.controls.IAndroidComposite#applyLayout(java.lang.String)
+ */
+ public void applyLayout(String layoutName)
+ {
+ //do nothing
+ }
+
+ /**
+ * Gets the listener that handles SWT key pressing and releasing events.
+ *
+ * @return the KeyListener object
+ */
+ public KeyListener getKeyListener()
+ {
+ return keyListener;
+ }
+
+ /**
+ * Gets the listener that handles SWT mouse clicking events.
+ *
+ * @return the MouseListener object
+ */
+ public MouseListener getMouseListener()
+ {
+ return mouseListener;
+ }
+
+ /**
+ * Gets the listener that handles SWT mouse moving events.
+ *
+ * @return the MouseMoveListener object
+ */
+ public MouseMoveListener getMouseMoveListener()
+ {
+ return mouseMoveListener;
+ }
+
+ /**
+ * Handles the mouse up event on the skin composite
+ *
+ * @param e
+ * The mouse up event
+ */
+ private void handleMouseUp(MouseEvent e)
+ {
+ if (e.button == 1)
+ {
+ isMouseLeftButtonPressed = false;
+ UIHelper.ajustCoordinates(e, this);
+ androidInput.sendMouseUp(e.x, e.y);
+ }
+ }
+
+ /**
+ * Handles the mouse down event on the skin composite
+ *
+ * @param e
+ * The mouse down event
+ */
+ private void handleMouseDown(MouseEvent e)
+ {
+ if (e.button == 1)
+ {
+ UIHelper.ajustCoordinates(e, this);
+ androidInput.sendMouseDown(e.x, e.y);
+ isMouseLeftButtonPressed = true;
+ }
+
+ }
+
+ /**
+ * Handles the mouse move event on the skin composite
+ *
+ * @param e
+ * The mouse move event
+ */
+ private void handleMouseMove(MouseEvent e)
+ {
+ if (isMouseLeftButtonPressed)
+ {
+ UIHelper.ajustCoordinates(e, this);
+ androidInput.sendMouseMove(e.x, e.y);
+ }
+ }
+
+ public boolean isFitToWindowSelected()
+ {
+ return isFitToWindow;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/nativewindow/NativeWindowComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/nativewindow/NativeWindowComposite.java
new file mode 100644
index 0000000..1658f98
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/nativewindow/NativeWindowComposite.java
@@ -0,0 +1,621 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.controls.nativewindow;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.model.IInputLogic;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.core.utils.TelnetAndroidInput;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.AndroidLogicUtils;
+import com.motorola.studio.android.emulator.ui.controls.IAndroidComposite;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+public class NativeWindowComposite extends ScrolledComposite implements IAndroidComposite
+{
+ /**
+ * Preference key of the Question Dialog about changing zoom
+ *
+ */
+ private static String LOOSE_ORIGINAL_SCALE_KEY_PREFERENCE = "loose.original.scale";
+
+ //Constants
+ private static final double MINIMUM_ZOOM_FACTOR = 0.10;
+
+ private static final double ZOOM_FIT = 0.0;
+
+ private Composite contentComposite;
+
+ private IAndroidEmulatorInstance androidInstance;
+
+ private long windowHandle;
+
+ private long originalParentHandle;
+
+ private long windowProperties;
+
+ private Point windowSize;
+
+ private Point nativeWindowSize;
+
+ private NativeWindowMonitor nativeWindowMonitor;
+
+ protected boolean resizing;
+
+ private boolean isFitToWindow;
+
+ private double zoomFactor = 0.99;
+
+ private double fitZoomFactor = ZOOM_FIT;
+
+ private boolean forceNativeWindowSizeUpdate;
+
+ private boolean isOriginalScale;
+
+ private boolean zoomLocked;
+
+ private class NativeWindowMonitor extends Timer
+ {
+ private Timer timer;
+
+ private MonitorTask monitorTask;
+
+ public NativeWindowMonitor(long interval)
+ {
+ timer = new Timer();
+ monitorTask = new MonitorTask();
+ timer.schedule(monitorTask, interval, interval);
+ }
+
+ private class MonitorTask extends TimerTask
+ {
+ @Override
+ public void run()
+ {
+ Point newWindowSize =
+ NativeUIUtils.getWindowSize(originalParentHandle, windowHandle);
+ if ((windowHandle <= 0) || !newWindowSize.equals(windowSize))
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ try
+ {
+ display.syncExec(new Runnable()
+ {
+ public void run()
+ {
+ updateContentComposite();
+ }
+ });
+ }
+ catch (SWTException e)
+ {
+ //Do nothing in case the widget is disposed, occurs when the tool is closing.
+ }
+ }
+ }
+
+ if (NativeUIUtils.isWindowEnabled(windowHandle))
+ {
+ Display display = Display.getDefault();
+ if (!display.isDisposed())
+ {
+ try
+ {
+ display.syncExec(new Runnable()
+ {
+ public void run()
+ {
+ if (!contentComposite.isDisposed())
+ {
+ contentComposite.forceFocus();
+ }
+ }
+ });
+ }
+ catch (SWTException e)
+ {
+ //Do nothing in case the widget is disposed, occurs when the tool is closing.
+ }
+ }
+ }
+ }
+ }
+
+ public void stopMonitoring()
+ {
+ timer.cancel();
+ timer = null;
+ monitorTask = null;
+ }
+ }
+
+ public NativeWindowComposite(Composite parent, IAndroidSkin androidSkin,
+ final IAndroidEmulatorInstance instance)
+ {
+ super(parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
+
+ info("Creating Native Window Composite for " + instance.getName());
+
+ getVerticalBar().setEnabled(true);
+ getHorizontalBar().setEnabled(true);
+ this.setLayout(new FillLayout());
+
+ androidInstance = instance;
+
+ nativeWindowMonitor = new NativeWindowMonitor(500);
+
+ addControlListener(new ControlAdapter()
+ {
+ final boolean[] running = new boolean[1];
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.ControlAdapter#controlResized(org.eclipse.swt.events.ControlEvent)
+ */
+ @Override
+ public void controlResized(ControlEvent event)
+ {
+ if (isFitToWindow)
+ {
+ try
+ {
+ Thread.sleep(200);
+ }
+ catch (InterruptedException e)
+ {
+ //do nothing
+ }
+
+ if (running[0])
+ {
+ return;
+ }
+ running[0] = true;
+ Display.getCurrent().asyncExec(new Runnable()
+ {
+ public void run()
+ {
+ running[0] = false;
+ if (!getShell().isDisposed())
+ {
+ calculateFitZoomFactor(forceNativeWindowSizeUpdate);
+ applyZoomFactor();
+ }
+
+ }
+ });
+ }
+ }
+ });
+
+ createContentComposite(instance);
+ info("Created Native Window Composite for " + instance.getName());
+ }
+
+ /**
+ * Creates the content composite that will be parent of emulator native window
+ *
+ * @param instance A android instance from which the composite could be retrieved if it is already created
+ */
+ public void createContentComposite(IAndroidEmulatorInstance instance)
+ {
+ contentComposite = instance.getComposite();
+ if (contentComposite != null)
+ {
+ info("Instance already has a composite");
+ contentComposite.setParent(this);
+ contentComposite.setVisible(true);
+ }
+ else
+ {
+ contentComposite = new Composite(this, SWT.EMBEDDED | SWT.NO_BACKGROUND);
+ }
+
+ this.setContent(contentComposite);
+ if (instance.getProperties().getProperty("Command_Line").contains("-scale"))
+ {
+ isOriginalScale = true;
+ }
+
+ //Force to update native window size at 100% when first using nativeWindowSize field
+ forceNativeWindowSizeUpdate = true;
+
+ //Avoid perform apply zoom factor when creating composite
+ zoomLocked = true;
+ draw();
+ }
+
+ /**
+ * Changes the parent from OS to content composite keeping the original properties and parent window reference
+ */
+ private void draw()
+ {
+ if (contentComposite != null)
+ {
+ windowHandle = androidInstance.getWindowHandle();
+
+ //If the instance does not contain the window handle, it should be retrieved
+ //from native emulator window and assigned to instance
+ if (windowHandle <= 0)
+ {
+ int port =
+ AndroidLogicUtils.getEmulatorPort(DDMSFacade
+ .getSerialNumberByName(androidInstance.getName()));
+ windowHandle = NativeUIUtils.getWindowHandle(androidInstance.getName(), port);
+
+ androidInstance.setWindowHandle(windowHandle);
+ }
+
+ if ((windowProperties <= 0) && (windowHandle > 0))
+ {
+ windowProperties = NativeUIUtils.getWindowProperties(windowHandle);
+ info("Native Window Properties:" + windowProperties);
+ }
+
+ //Set Window Style
+ if (windowHandle > 0)
+ {
+ NativeUIUtils.setWindowStyle(windowHandle);
+ }
+
+ if (originalParentHandle <= 0)
+ {
+ originalParentHandle = windowHandle;
+ }
+
+ //Retrieve window size before changing parent
+ if (windowHandle > 0)
+ {
+ windowSize = NativeUIUtils.getWindowSize(originalParentHandle, windowHandle);
+ }
+
+ //Set the new Parent and store the original parent
+ if ((originalParentHandle <= 0) || (originalParentHandle == windowHandle))
+ {
+ if (windowHandle > 0)
+ {
+ originalParentHandle =
+ NativeUIUtils.embedWindow(windowHandle, contentComposite);
+ info("Native Window Parent:" + originalParentHandle);
+ }
+ }
+ else
+ {
+ NativeUIUtils.embedWindow(windowHandle, contentComposite);
+ }
+
+ if (windowSize == null)
+ {
+ windowSize = new Point(700, 500);
+ }
+
+ //Update composite size
+ contentComposite
+ .setSize(contentComposite.computeSize(windowSize.x, windowSize.y, true));
+ contentComposite.redraw();
+ this.update();
+
+ this.setMinSize(contentComposite.computeSize(windowSize.x, windowSize.y));
+ this.layout();
+ }
+ else
+ {
+ createContentComposite(androidInstance);
+ }
+ }
+
+ public void changeToNextLayout()
+ {
+ contentComposite.setVisible(false);
+
+ contentComposite.setLocation(0, 0);
+
+ NativeUIUtils.sendNextLayoutCommand(originalParentHandle, windowHandle);
+
+ updateContentComposite();
+
+ forceNativeWindowSizeUpdate = true;
+ if (isFitToWindow)
+ {
+ //Force update to fit zoom factor
+ setZoomFactor(ZOOM_FIT);
+ }
+ applyZoomFactor();
+
+ try
+ {
+ Thread.sleep(200);
+ }
+ catch (InterruptedException e)
+ {
+ //do nothing
+ }
+
+ contentComposite.setVisible(true);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Widget#dispose()
+ */
+ @Override
+ public void dispose()
+ {
+ info("Disposing Native Window Composite");
+ if (nativeWindowMonitor != null)
+ {
+ nativeWindowMonitor.stopMonitoring();
+ nativeWindowMonitor = null;
+ info("Disposed Native Window Monitor");
+ }
+ if (windowHandle > 0)
+ {
+ info("Restoring original properties for window: " + windowHandle);
+ NativeUIUtils.setWindowProperties(windowHandle, windowProperties);
+
+ boolean shallUnembed =
+ AndroidPlugin.getDefault().getPreferenceStore()
+ .getBoolean(AndroidPlugin.SHALL_UNEMBED_EMULATORS_PREF_KEY);
+ if ((originalParentHandle > 0) && shallUnembed)
+ {
+ info("Setting original parent: " + originalParentHandle + " for window"
+ + windowHandle);
+ NativeUIUtils.unembedWindow(windowHandle, originalParentHandle);
+ //Force update when redrawing
+ androidInstance.setWindowHandle(0);
+ info("Restoring window: " + windowHandle);
+ NativeUIUtils.restoreWindow(windowHandle);
+ }
+
+ }
+
+ if (!Platform.getOS().equals(Platform.OS_WIN32))
+ {
+ info("Trying to store the content composite in instance");
+ if (contentComposite != null)
+ {
+ info("Is instance started? :" + androidInstance.isStarted());
+ if (androidInstance.isStarted())
+ {
+ try
+ {
+ contentComposite.setParent(PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getShell());
+ this.setContent(null);
+ contentComposite.setVisible(true);
+ androidInstance.setComposite(contentComposite);
+ }
+ catch (Exception e)
+ {
+ error("Error trying to store the content composite :" + e.getMessage());
+ }
+ }
+ }
+ }
+ super.dispose();
+ }
+
+ /**
+ * Apply the zoom factor to the instance
+ */
+ public void applyZoomFactor()
+ {
+ if (!isOriginalScale && !zoomLocked)
+ {
+ contentComposite.setLocation(0, 0);
+ IInputLogic inputLogic = androidInstance.getInputLogic();
+ TelnetAndroidInput telnetAndroidInput = (TelnetAndroidInput) inputLogic;
+ NativeUIUtils.hideWindow(windowHandle);
+
+ if (isFitToWindow)
+ {
+ telnetAndroidInput.sendWindowScale(fitZoomFactor);
+ }
+ else
+ {
+ telnetAndroidInput.sendWindowScale(zoomFactor);
+ }
+
+ try
+ {
+ Thread.sleep(200);
+ }
+ catch (InterruptedException e)
+ {
+ //do nothing
+ }
+ telnetAndroidInput.dispose();
+ NativeUIUtils.showWindow(windowHandle);
+ updateContentComposite();
+ }
+ }
+
+ void calculateFitZoomFactor(boolean requireNativeSizeUpdate)
+ {
+ // Compute new zoom factor if the zoom mode is "Fit to Window"
+ Rectangle clientArea = getClientArea();
+ if ((clientArea.width == 0) || (clientArea.height == 0))
+ {
+ // zoom factor cannot be zero, otherwise an
+ // IllegalArgumentException
+ // is raised in some SWT methods
+ fitZoomFactor = MINIMUM_ZOOM_FACTOR;
+ }
+ else
+ {
+ // if the layout was changed, it is needed to retrieve the native window size at 100%
+ // that size is required to the correct ratio calculus
+ if (requireNativeSizeUpdate)
+ {
+ forceNativeWindowSizeUpdate = false;
+ updateNativeWindowSize();
+ }
+ else
+ {
+ double widthRatio = (double) (clientArea.width) / nativeWindowSize.x;
+ double heightRatio = (double) (clientArea.height) / nativeWindowSize.y;
+ fitZoomFactor =
+ (Math.min(widthRatio, heightRatio) > MINIMUM_ZOOM_FACTOR ? Math.min(
+ widthRatio, heightRatio) : MINIMUM_ZOOM_FACTOR);
+ }
+ }
+ }
+
+ /**
+ * This method brings the emulator window to 100% zoom factor to retrieve their native size
+ */
+ private void updateNativeWindowSize()
+ {
+ info("Updating Native Window Size");
+ setZoomFactor(1.0d);
+ applyZoomFactor();
+
+ nativeWindowSize = NativeUIUtils.getWindowSize(originalParentHandle, windowHandle);
+
+ setZoomFactor(ZOOM_FIT);
+ applyZoomFactor();
+ info("Updated Native Window Size");
+ }
+
+ private void updateContentComposite()
+ {
+ if (!this.isDisposed())
+ {
+ windowSize = NativeUIUtils.getWindowSize(originalParentHandle, windowHandle);
+ if (windowSize != null)
+ {
+ if ((contentComposite != null) && !contentComposite.isDisposed())
+ {
+ contentComposite.setSize(windowSize.x, windowSize.y);
+ contentComposite.redraw();
+ }
+ this.setMinSize(windowSize.x, windowSize.y);
+ draw();
+ this.redraw();
+ info("Updated Content Composite");
+ }
+ }
+ }
+
+ /**
+ * Gets the current zoom factor.
+ *
+ * @return the zoom factor
+ */
+ public double getZoomFactor()
+ {
+ if (isFitToWindow)
+ {
+ return fitZoomFactor;
+ }
+ return zoomFactor;
+ }
+
+ /**
+ * Sets the zoom factor.
+ *
+ * @param zoom the zoom factor
+ *
+ */
+ public void setZoomFactor(double zoom)
+ {
+ boolean execute = true;
+ zoomLocked = false;
+ if (isOriginalScale)
+ {
+ execute =
+ DialogWithToggleUtils.showQuestion(LOOSE_ORIGINAL_SCALE_KEY_PREFERENCE,
+ EmulatorNLS.QUESTION_NativeWindow_LooseOriginalScale_Title,
+ EmulatorNLS.QUESTION_NativeWindow_LooseOriginalScale_Text);
+ }
+ if (execute)
+ {
+ isOriginalScale = false;
+
+ if (zoom == ZOOM_FIT)
+ {
+ isFitToWindow = true;
+ calculateFitZoomFactor(forceNativeWindowSizeUpdate);
+ }
+ else
+ {
+ isOriginalScale = false;
+ isFitToWindow = false;
+ }
+ zoomFactor = zoom;
+ }
+ }
+
+ @Override
+ public boolean setFocus()
+ {
+ NativeUIUtils.setWindowFocus(windowHandle);
+ return super.setFocus();
+ }
+
+ public void applyLayout(String layoutName)
+ {
+ setLayout(new FillLayout());
+ draw();
+ }
+
+ public KeyListener getKeyListener()
+ {
+ return null;
+ }
+
+ public MouseListener getMouseListener()
+ {
+ return null;
+ }
+
+ public MouseMoveListener getMouseMoveListener()
+ {
+ return null;
+ }
+
+ public boolean isFitToWindowSelected()
+ {
+ return isFitToWindow;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/skin/AndroidSkinLayout.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/skin/AndroidSkinLayout.java
new file mode 100644
index 0000000..11fadd5
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/skin/AndroidSkinLayout.java
@@ -0,0 +1,507 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.controls.skin;
+
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Layout;
+
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.skin.AndroidSkinBean;
+import com.motorola.studio.android.emulator.core.skin.ISkinKeyXmlTags;
+import com.motorola.studio.android.emulator.ui.IAndroidUIConstants;
+import com.motorola.studio.android.emulator.ui.controls.RemoteCLIDisplay;
+import com.motorola.studio.android.emulator.ui.controls.UIHelper;
+
+/**
+ * DESCRIPTION:
+ * This class implements the layout used to draw skins
+ * It is a very specific implementation that applies only to Android Emulator
+ * The position and size of the widgets are read from the skin bean provided
+ * during layout construction
+ *
+ * RESPONSIBILITY:
+ * - Draw the widgets that composes the skin in the correct position and
+ * with the correct size
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * Use of this class is restricted to the classes in this package
+ */
+class AndroidSkinLayout extends Layout
+{
+ /**
+ * Object that holds information from the skin.xml file
+ * of the skin being currently used
+ */
+ private final AndroidSkinBean skin;
+
+ /**
+ * Reference to the CLI display that will be placed by this layout
+ */
+ private RemoteCLIDisplay cliDisplayChild = null;
+
+ /**
+ * Reference to the main display that will be placed by this layout
+ */
+ private SWTRemoteDisplay mainDisplayChild = null;
+
+ /**
+ * Flag that indicates if the skin contains an open external display
+ * placeholder
+ */
+ private final boolean openExternalDisplayAvailable;
+
+ /**
+ * Flag that indicates if the skin contains an external display
+ * placeholder
+ */
+ private final boolean externalDisplayAvailable;
+
+ /**
+ * Flag that indicates that the flip is supported. This flag has nothing to do with slide
+ */
+ private final boolean isFlipSupported;
+
+ /**
+ * Creates a new AndroidSkinLayout object.
+ * As the image dimensions are not provided with this constructor, it is not possible
+ * to have automatic zoom factor calculation. Using this constructor will set the
+ * initial zoom policy to "100%"
+ *
+ * @param skin An skin bean containing the parameters used to place the
+ * widgets
+ * @param isFlipSupported Flag that indicates if flip (not slide) is supported
+ */
+ AndroidSkinLayout(AndroidSkinBean skin, boolean isFlipSupported)
+ {
+ this.skin = skin;
+
+ this.isFlipSupported = isFlipSupported;
+ openExternalDisplayAvailable = skin.isOpenExternalDisplayAvailable();
+ externalDisplayAvailable = skin.isExternalDisplayAvailable();
+ }
+
+ /**
+ * @see org.eclipse.swt.widgets.Layout#computeSize(Composite, int, int, boolean)
+ */
+ @Override
+ protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache)
+ {
+ if (!(composite instanceof SkinComposite))
+ {
+ throw new IllegalArgumentException();
+ }
+
+ Point size;
+
+ // Retrieve needed data from composite
+ IAndroidEmulatorInstance instance = UIHelper.getInstanceAssociatedToControl(composite);
+ boolean flipSlideClosed = false;
+ if (instance != null)
+ {
+ //flipSlideClosed = instance.isFlipSlideClosed();
+ flipSlideClosed = false;
+ }
+ double zoomFactor = ((SkinComposite) composite).getZoomFactor();
+
+ if (externalDisplayAvailable)
+ {
+ if (openExternalDisplayAvailable)
+ {
+ if (!flipSlideClosed)
+ {
+ // Scenario 1:
+ // A) Available displays: INTERNAL, OPEN EXTERNAL, EXTERNAL
+ // B) Flip is open
+ // C) Displays being showed: INTERNAL, OPEN EXTERNAL
+ size = allAvailable(zoomFactor);
+ }
+ else
+ {
+ // Scenario 2:
+ // A) Available displays: INTERNAL, OPEN EXTERNAL, EXTERNAL
+ // B) Flip is closed
+ // C) Display being showed: EXTERNAL
+ size = flipClosed(zoomFactor);
+ }
+ }
+ else
+ {
+ if (!flipSlideClosed)
+ {
+ // Scenario 3:
+ // A) Available displays: INTERNAL, EXTERNAL
+ // B) Flip is opened
+ // C) Display being showed: INTERNAL
+ size = openExternalUnavailable(zoomFactor);
+ }
+ else
+ {
+ // Scenario 4:
+ // A) Available displays: INTERNAL, EXTERNAL
+ // B) Flip is closed
+ // C) Display being showed: EXTERNAL
+ size = openExternalUnavailableAndFlipClosed(zoomFactor);
+ }
+ }
+ }
+ else
+ {
+ // Scenario 5:
+ // A) Available display: INTERNAL
+ // B) Flip is opened or closed
+ // C) Display being showed: INTERNAL
+ size = onlyInternal(zoomFactor);
+ }
+
+ return size;
+ }
+
+ /**
+ * This method is called by computeSize when all displays are available and the flip is opened
+ *
+ * @param zoomFactor The zoom factor used to calculate the size
+ *
+ * @return The size
+ */
+ private Point allAvailable(double zoomFactor)
+ {
+ Point size;
+
+ int x1 = skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_X);
+ int y1 =
+ Math.min(skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_Y),
+ skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_Y));
+ int x2 =
+ skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_X)
+ + skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_WIDTH);
+ int y2 =
+ Math.max(
+ skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_Y)
+ + skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT),
+ skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_Y)
+ + skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_HEIGHT));
+
+ size = new Point((int) ((x2 - x1) * zoomFactor), (int) ((y2 - y1) * zoomFactor));
+
+ return size;
+ }
+
+ /**
+ * This method is called by computeSize when all displays are available and the flip is closed
+ *
+ * @param zoomFactor The zoom factor used to calculate the size
+ *
+ * @return The size
+ */
+ private Point flipClosed(double zoomFactor)
+ {
+ Point size =
+ new Point(
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_WIDTH) * zoomFactor),
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_HEIGHT) * zoomFactor));
+
+ return size;
+ }
+
+ /**
+ * This method is called by computeSize when all displays but open external display are available
+ * and the flip is opened
+ *
+ * @param zoomFactor The zoom factor used to calculate the size
+ *
+ * @return The size
+ */
+ private Point openExternalUnavailable(double zoomFactor)
+ {
+ Point size =
+ new Point(
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH) * zoomFactor),
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT) * zoomFactor));
+
+ return size;
+ }
+
+ /**
+ * This method is called by computeSize when all displays but open external display are available
+ * and the flip is closed
+ *
+ * @param zoomFactor The zoom factor used to calculate the size
+ *
+ * @return The size
+ */
+ private Point openExternalUnavailableAndFlipClosed(double zoomFactor)
+ {
+ Point size =
+ new Point(
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_WIDTH) * zoomFactor),
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_HEIGHT) * zoomFactor));
+
+ return size;
+ }
+
+ /**
+ * This method is called by computeSize when only the internal display is available
+ *
+ * @param zoomFactor The zoom factor used to calculate the size
+ *
+ * @return The size
+ */
+ private Point onlyInternal(double zoomFactor)
+ {
+ Point size =
+ new Point(
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH) * zoomFactor),
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT) * zoomFactor));
+
+ return size;
+ }
+
+ /**
+ * @see org.eclipse.swt.widgets.Layout#layout(Composite, boolean)
+ */
+ @Override
+ protected void layout(Composite composite, boolean flushCache)
+ {
+ if (!(composite instanceof SkinComposite))
+ {
+ // If this composite is not a SkinComposite, no layout should be done
+ return;
+ }
+
+ boolean canProceed = true;
+
+ if (flushCache || (mainDisplayChild == null) || (cliDisplayChild == null))
+ {
+ Control[] children = composite.getChildren();
+ canProceed = checkChidren(children);
+ }
+
+ if (canProceed)
+ {
+ // Retrieve needed data from composite
+ SkinComposite skinComposite = (SkinComposite) composite;
+ IAndroidEmulatorInstance instance = UIHelper.getInstanceAssociatedToControl(composite);
+ boolean flipSlideClosed = false;
+ if (instance != null)
+ {
+ //flipSlideClosed = instance.isFlipSlideClosed();
+ flipSlideClosed = false;
+ }
+
+ skinComposite.recalculateZoomFactor();
+ skinComposite.updateDisplayRectangle();
+
+ double zoomFactor = skinComposite.getZoomFactor();
+ Rectangle displayRectangle = skinComposite.getDisplayRectangle();
+
+ // Handling main display case
+ if (mainDisplayChild != null)
+ {
+ layoutMainDisplay(zoomFactor, displayRectangle);
+
+ if ((isFlipSupported) && (flipSlideClosed))
+ {
+ // On phones that support flip, the main display is hidden
+ // when the flip is closed
+ mainDisplayChild.setVisible(false);
+ }
+ else
+ {
+ mainDisplayChild.setVisible(true);
+ }
+ }
+
+ // Handling CLI display case
+ if (cliDisplayChild != null)
+ {
+ layoutCliDisplay(zoomFactor, displayRectangle, flipSlideClosed);
+
+ if (((externalDisplayAvailable) && (!flipSlideClosed))
+ || ((openExternalDisplayAvailable) && (flipSlideClosed)))
+ {
+ // The CLI display is shown in 2 situations:
+ // 1. When the flip is closed and there is information about
+ // external display
+ // 2. When the flip is opened and there is information about
+ // open external display
+ cliDisplayChild.setVisible(true);
+ }
+ else
+ {
+ cliDisplayChild.setVisible(false);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Checks if the composite children are as expected by the layout
+ * It is needed to check if the composite's children are, at most:
+ * 1. One main display
+ * 2. One CLI display
+ *
+ * @param children Array of composite children to check
+ *
+ * @return true if children are as expected; false otherwise
+ */
+ private boolean checkChidren(Control[] children)
+ {
+
+ RemoteCLIDisplay cliDisplayInstance = null;
+ SWTRemoteDisplay mainDisplayInstance = null;
+ boolean childrenOk = true;
+
+ // We need to check if the composite's children are, at most:
+ //
+ // 1. One main display
+ // 2. One CLI display
+ for (Control child : children)
+ {
+ if (child instanceof SWTRemoteDisplay)
+ {
+ if (mainDisplayInstance == null)
+ {
+ mainDisplayInstance = (SWTRemoteDisplay) child;
+ mainDisplayChild = mainDisplayInstance;
+ }
+ else
+ {
+ childrenOk = false;
+
+ break;
+ }
+ }
+ else if (child instanceof RemoteCLIDisplay)
+ {
+ if (cliDisplayInstance == null)
+ {
+ cliDisplayInstance = (RemoteCLIDisplay) child;
+ cliDisplayChild = cliDisplayInstance;
+ }
+ else
+ {
+ childrenOk = false;
+
+ break;
+ }
+ }
+ else
+ {
+ childrenOk = false;
+
+ break;
+ }
+ }
+
+ return childrenOk;
+ }
+
+ /**
+ * Performs the layout for main display
+ *
+ * @param zoomFactor The zoom factor to use when dimensioning the main display.
+ * @param displayRectangle The area of skin that is being shown at the view. It can
+ * be only a part of the skin if the view client area
+ * is smaller than the skin size. It is used to calculate translation.
+ */
+ private void layoutMainDisplay(double zoomFactor, Rectangle displayRectangle)
+ {
+ // Computes main display position
+ int x =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_X) * zoomFactor)
+ - displayRectangle.x;
+ int y =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_Y) * zoomFactor)
+ - displayRectangle.y;
+ int width =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH)
+ * zoomFactor * skin.getEmbeddedViewScale());
+ int height =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT)
+ * zoomFactor * skin.getEmbeddedViewScale());
+
+ // Sets main display size and position
+ mainDisplayChild.setZoomFactor(zoomFactor * skin.getEmbeddedViewScale());
+ mainDisplayChild.setBounds(x, y, width, height);
+ }
+
+ /**
+ * Performs the layout for CLI display
+ *
+ * @param zoomFactor The zoom factor to use when dimensioning the main display
+ * @param displayRectangle The area of skin that is being shown at the view. It can
+ * be only a part of the skin if the view client area
+ * is smaller than the skin size. It is used to calculate translation.
+ * @param isFlipSlideClosed True if the flip or slide is currently closed. False otherwise.
+ */
+ private void layoutCliDisplay(double zoomFactor, Rectangle displayRectangle,
+ boolean flipSlideClosed)
+ {
+ // Computes CLI display position
+ int x = 0;
+ int y = 0;
+ int width = 0;
+ int height = 0;
+
+ if (!flipSlideClosed && openExternalDisplayAvailable)
+ {
+ x =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_X) * zoomFactor)
+ - displayRectangle.x;
+ y =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_Y) * zoomFactor)
+ - displayRectangle.y;
+ width =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_WIDTH)
+ * zoomFactor * IAndroidUIConstants.DISPLAY_TO_SKIN_RATIO);
+ height =
+ (int) (skin
+ .getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_HEIGHT)
+ * zoomFactor * IAndroidUIConstants.DISPLAY_TO_SKIN_RATIO);
+ }
+ else if (flipSlideClosed && externalDisplayAvailable)
+ {
+ x =
+ (int) ((skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_X) * zoomFactor) - displayRectangle.x);
+ y =
+ (int) ((skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_Y) * zoomFactor) - displayRectangle.y);
+ width =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_WIDTH)
+ * zoomFactor * IAndroidUIConstants.DISPLAY_TO_SKIN_RATIO);
+ height =
+ (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_HEIGHT)
+ * zoomFactor * IAndroidUIConstants.DISPLAY_TO_SKIN_RATIO);
+ }
+
+ // Sets cli display size
+ cliDisplayChild.setZoomFactor(zoomFactor * IAndroidUIConstants.DISPLAY_TO_SKIN_RATIO);
+ cliDisplayChild.setBounds(x, y, width, height);
+ }
+
+ void dispose()
+ {
+ cliDisplayChild = null;
+ mainDisplayChild = null;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/skin/SkinComposite.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/skin/SkinComposite.java
new file mode 100644
index 0000000..fa50267
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/controls/skin/SkinComposite.java
@@ -0,0 +1,1300 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.controls.skin;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.Collection;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackAdapter;
+import org.eclipse.swt.events.MouseWheelListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Layout;
+import org.eclipse.swt.widgets.ScrollBar;
+import org.eclipse.ui.PlatformUI;
+
+import com.motorola.studio.android.adt.DDMSFacade;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.model.IInputLogic;
+import com.motorola.studio.android.emulator.core.skin.AndroidPressKey;
+import com.motorola.studio.android.emulator.core.skin.AndroidSkinBean;
+import com.motorola.studio.android.emulator.core.skin.IAndroidKey;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.AndroidLogicUtils;
+import com.motorola.studio.android.emulator.ui.controls.IAndroidComposite;
+import com.motorola.studio.android.emulator.ui.controls.UIHelper;
+import com.motorola.studio.android.emulator.ui.handlers.IHandlerConstants;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * DESCRIPTION: This class implements the UI part of the skin
+ *
+ * RESPONSIBILITY: - Provide the skin image with correct layout - Provide means
+ * for the user to send events to the emulator through the phone emulated
+ * keyboard - Provide means for the user to change the zoom and window scrolling
+ * properties
+ *
+ * COLABORATORS: None.
+ *
+ * USAGE: Create an instance of this class every time an phone instance is to be
+ * displayed Call the public methods to interact with the skin.
+ */
+public class SkinComposite extends Composite implements IAndroidComposite
+{
+ // Constants for defining interval between keystrokes
+ // when using long mouse click
+ public static final int FIRST_REFRESH_DELAY_MS = 300;
+
+ public static final int REFRESH_DELAY_PERIOD_MS = 100;
+
+ // Minimum value to be used as zoom factor. This is necessary to avoid
+ // divisions to zero
+ private static final double MINIMUM_ZOOM_FACTOR = 0.0001;
+
+ // The step increased or decreased from the zoom factor using mouse wheel
+ private static final double ZOOM_STEP = 0.5;
+
+ /**
+ * A rectangle that represents the part of the currentSkin image that fits
+ * into the view/screen. It must fit inside the currentSkinImage borders (0,
+ * 0, current skin image width, current skin image height)
+ */
+ private final Rectangle displayRectangle = new Rectangle(0, 0, 0, 0);
+
+ /**
+ * The skin elements provider
+ */
+ private IAndroidSkin skin;
+
+ /**
+ * The image that is currently drawn at screen. It is one image provided by
+ * the skin that is scaled by the current zoom factor
+ */
+ private Image currentSkinImage;
+
+ /**
+ * The key that mouse is over at the current moment
+ */
+ private IAndroidKey currentKey;
+
+ /**
+ * The flag indicating that Ctrl key is pressed
+ */
+ private boolean ctrlPressed = false;
+
+ /**
+ * SWT key pressed/released events listener
+ */
+ private KeyListener keyListener;
+
+ private MouseListener mainDisplayMouseListener;
+
+ private MouseMoveListener mainDisplayMouseMoveListener;
+
+ /**
+ * Flag that indicates if the skin can use the scroll bars to draw itself
+ * bigger than the view client area
+ */
+ private boolean scrollBarsUsed = true;
+
+ /**
+ * True if the mouse left button is pressed. False otherwise
+ */
+ private boolean isMouseLeftButtonPressed;
+
+ /**
+ * True if the mouse right button is pressed. False otherwise
+ */
+ private boolean isMouseRightButtonPressed;
+
+ /**
+ * The zoom factor whose default value is 1.0 (100%)
+ */
+ private double zoomFactor = 1.0;
+
+ private double embeddedViewScale = 1.0;
+
+ private IInputLogic androidInput;
+
+ private boolean isMouseMainDisplayLeftButtonPressed;
+
+ IAndroidEmulatorInstance androidInstance;
+
+ /**
+ * Creates a SkinComposite This composite holds the screens in the correct
+ * positions and maps the keys
+ *
+ * @param parent
+ * The parent composite in which the UI part of the instance
+ * shall be created
+ * @param androidSkin
+ * The skin object that contain data for getting skin information
+ */
+ public SkinComposite(Composite parent, IAndroidSkin androidSkin,
+ IAndroidEmulatorInstance instance)
+ {
+ super(parent, SWT.BACKGROUND | SWT.H_SCROLL | SWT.V_SCROLL);
+ skin = androidSkin;
+ androidInstance = instance;
+
+ // Add listeners
+ addListeners();
+ createListenersForMainDisplay();
+
+ setToolTipText(null);
+
+ androidInput = instance.getInputLogic();
+
+ // Init the scroll bars
+ initScrollBars();
+
+ if (!Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ hideEmulatorWindow();
+ }
+ }
+
+ private void hideEmulatorWindow()
+ {
+ int port =
+ AndroidLogicUtils.getEmulatorPort(DDMSFacade.getSerialNumberByName(androidInstance
+ .getName()));
+ long windowHandle = NativeUIUtils.getWindowHandle(androidInstance.getName(), port);
+ androidInstance.setWindowHandle(windowHandle);
+ NativeUIUtils.hideWindow(windowHandle);
+ }
+
+ /**
+ * Add listeners to the skin composite
+ */
+ private void addListeners()
+ {
+
+ addPaintListener(new PaintListener()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
+ */
+ public void paintControl(PaintEvent e)
+ {
+ // This listener is invoked when a regular SWT redraw is invoked. In this case, no keys
+ // have changed. That's why we pass "null" as parameter
+ drawSkin(e.gc, null);
+ }
+ });
+
+ addMouseListener(new MouseAdapter()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.MouseAdapter#mouseUp(org.eclipse.swt.events.MouseEvent)
+ */
+ @Override
+ public void mouseUp(MouseEvent e)
+ {
+ if (e.button == 1)
+ {
+ isMouseLeftButtonPressed = false;
+
+ }
+ else if (e.button == 3)
+ {
+ isMouseRightButtonPressed = false;
+ }
+
+ // Handle left button mouse up event
+ if (e.button == 1)
+ {
+ cancelMouseSelection();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.MouseAdapter#mouseDown(org.eclipse.swt.events.MouseEvent)
+ */
+ @Override
+ public void mouseDown(MouseEvent e)
+ {
+ setFocus();
+ if (e.button == 1)
+ {
+ isMouseLeftButtonPressed = true;
+ }
+ else if (e.button == 3)
+ {
+ isMouseRightButtonPressed = true;
+ }
+
+ if (currentKey != null)
+ {
+ ImageData mergedImage = getKeyImageData(currentKey, false);
+ setSkinImage(mergedImage, currentKey, true);
+
+ // Handle left button mouse down event
+ if ((e.button == 1) && (!isMouseRightButtonPressed) && (currentKey != null))
+ {
+ androidInput.sendClick(currentKey.getKeysym(), true);
+ }
+
+ // Handle right button mouse down event
+ else if (e.button == 3)
+ {
+ cancelMouseSelection();
+ }
+
+ }
+ }
+ });
+
+ addMouseMoveListener(new MouseMoveListener()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
+ */
+ public void mouseMove(MouseEvent e)
+ {
+ int posX = (int) ((e.x + displayRectangle.x) / zoomFactor);
+ int posY = (int) ((e.y + displayRectangle.y) / zoomFactor);
+ IAndroidKey keyData = getSkinKey(posX, posY);
+
+ if ((isMouseLeftButtonPressed) && (keyData != currentKey))
+ {
+ cancelMouseSelection();
+ }
+
+ if (!isMouseLeftButtonPressed && (currentKey != keyData))
+ {
+ changeCurrentKey(keyData);
+ }
+ }
+ });
+
+ // listener to change the zoom factor using Ctrl + Mouse Wheel
+ addMouseWheelListener(new MouseWheelListener()
+ {
+ public void mouseScrolled(MouseEvent event)
+ {
+ if (ctrlPressed)
+ {
+
+ // the floor and ceil are required if "fits to window" was
+ // checked.
+ double roundedZoomFactor = Math.floor(zoomFactor / ZOOM_STEP) * ZOOM_STEP;
+
+ if ((event.count > 0) && (roundedZoomFactor < IHandlerConstants.MAXIMUM_ZOOM))
+ {
+ // increase zoom factor
+ setZoomFactor(roundedZoomFactor + ZOOM_STEP);
+ applyZoomFactor();
+ }
+
+ else if ((event.count < 0)
+ && (roundedZoomFactor > IHandlerConstants.MINIMUM_ZOOM))
+ {
+ // decrease zoom factor
+ setZoomFactor(roundedZoomFactor - ZOOM_STEP);
+ applyZoomFactor();
+ }
+
+ }
+ }
+ });
+
+ addMouseTrackListener(new MouseTrackAdapter()
+ {
+ @Override
+ public void mouseExit(MouseEvent mouseevent)
+ {
+ changeCurrentKey(null);
+ }
+ });
+
+ addControlListener(new ControlAdapter()
+ {
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.events.ControlAdapter#controlResized(org.eclipse.swt.events.ControlEvent)
+ */
+ @Override
+ public void controlResized(ControlEvent event)
+ {
+ if (scrollBarsUsed)
+ {
+ synchronizeScrollBars();
+ }
+ else
+ {
+ ImageData imageData = getImageData(false, false);
+ setSkinImage(imageData, null, false);
+ }
+ }
+ });
+
+ }
+
+ public void applyLayout(String layoutName)
+ {
+ // Populate the attributes with information from skin
+ AndroidSkinBean skinBean = null;
+
+ try
+ {
+ skinBean = skin.getSkinBean(layoutName);
+ }
+ catch (SkinException e)
+ {
+ error("The skin data could not be retrieved from skin files. Cause: " + e.getMessage());
+ EclipseUtils.showErrorDialog(e);
+ }
+
+ // Create layout and set it to composite
+ if (skinBean != null)
+ {
+ // When changing to a new layout, the key may move to another position
+ // It does not make sense to keep the old key object
+ currentKey = null;
+
+ // Change the background color to the one that applies to the layout being set
+ RGB color = skin.getBackgroundColor(layoutName);
+ setBackground(new Color(PlatformUI.getWorkbench().getDisplay(), color));
+
+ Layout prevLayout = getLayout();
+ if (prevLayout instanceof AndroidSkinLayout)
+ {
+ ((AndroidSkinLayout) prevLayout).dispose();
+ }
+
+ AndroidSkinLayout androidLayout =
+ new AndroidSkinLayout(skinBean, skin.isFlipSupported());
+ setLayout(androidLayout);
+
+ embeddedViewScale = skinBean.getEmbeddedViewScale();
+
+ layout();
+ redraw();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.swt.widgets.Widget#dispose()
+ */
+ @Override
+ public void dispose()
+ {
+ if (androidInput != null)
+ {
+ androidInput.dispose();
+ }
+
+ if (currentSkinImage != null)
+ {
+ currentSkinImage.dispose();
+ }
+
+ Layout layout = getLayout();
+ if (layout instanceof AndroidSkinLayout)
+ {
+ ((AndroidSkinLayout) layout).dispose();
+ }
+
+ skin = null;
+ currentKey = null;
+ keyListener = null;
+ mainDisplayMouseListener = null;
+ mainDisplayMouseMoveListener = null;
+
+ if (!Platform.getOS().equals(Platform.OS_MACOSX))
+ {
+ long hnd = androidInstance.getWindowHandle();
+ if (hnd > 0)
+ {
+ NativeUIUtils.showWindow(hnd);
+ NativeUIUtils.restoreWindow(hnd);
+ }
+
+ //Force update on redrawing
+ androidInstance.setWindowHandle(0);
+ }
+
+ super.dispose();
+ }
+
+ /**
+ * Sets the zoom factor to use in the instance
+ */
+ public void applyZoomFactor()
+ {
+ if (getZoomFactor() == 0)
+ {
+ scrollBarsUsed = false;
+ getVerticalBar().setEnabled(false);
+ getHorizontalBar().setEnabled(false);
+ }
+ else
+ {
+ scrollBarsUsed = true;
+ getVerticalBar().setEnabled(true);
+ getHorizontalBar().setEnabled(true);
+ }
+
+ // Resets translation
+ displayRectangle.x = 0;
+ displayRectangle.y = 0;
+
+ redrawReleasedImage();
+
+ }
+
+ /**
+ * Sets the flip/slide status of the phone
+ */
+ private void redrawReleasedImage()
+ {
+ if (currentSkinImage != null)
+ {
+ ImageData imageData = getImageData(false, false);
+ setSkinImage(imageData, null, false);
+ layout();
+ redraw();
+ }
+
+ if (scrollBarsUsed)
+ {
+ synchronizeScrollBars();
+ }
+ }
+
+ /**
+ * Performs the skin draw operation.
+ *
+ * @param gcUsedToDraw
+ * The gc object associated with the skin composite that is used
+ * to draw the images
+ */
+ private void drawSkin(GC gcUsedToDraw, IAndroidKey changedKey)
+ {
+ if (currentSkinImage == null)
+ {
+ IAndroidEmulatorInstance instance = UIHelper.getInstanceAssociatedToControl(this);
+ ImageData initialSkinImage = getImageData(false, false);
+ setSkinImage(initialSkinImage, null, false);
+ applyLayout(instance.getCurrentLayout());
+
+ if (scrollBarsUsed)
+ {
+ synchronizeScrollBars();
+ }
+ }
+
+ if (displayRectangle != null)
+ {
+ int srcXPos, srcYPos, srcWidth, srcHeight;
+ int destXPos, destYPos, destWidth, destHeight;
+ if (changedKey == null)
+ {
+ srcXPos = displayRectangle.x;
+ srcYPos = displayRectangle.y;
+ srcWidth = displayRectangle.width;
+ srcHeight = displayRectangle.height;
+ destXPos = 0;
+ destYPos = 0;
+ destWidth = Math.min(currentSkinImage.getImageData().width, displayRectangle.width);
+ destHeight =
+ Math.min(currentSkinImage.getImageData().height, displayRectangle.height);
+ }
+ else
+ {
+ srcXPos =
+ ((int) (changedKey.getKeyArea().x > 0 ? changedKey.getKeyArea().x * zoomFactor
+ : 0));
+ srcYPos =
+ ((int) (changedKey.getKeyArea().y > 0 ? changedKey.getKeyArea().y * zoomFactor
+ : 0));
+ srcWidth = ((int) (changedKey.getKeyArea().width * zoomFactor));
+ srcHeight = ((int) (changedKey.getKeyArea().height * zoomFactor));
+ destXPos = srcXPos - displayRectangle.x;
+ destYPos = srcYPos - displayRectangle.y;
+ destWidth = srcWidth;
+ destHeight = srcHeight;
+ }
+
+ gcUsedToDraw.drawImage(currentSkinImage, srcXPos, srcYPos, srcWidth, srcHeight,
+ destXPos, destYPos, destWidth, destHeight);
+ }
+ }
+
+ /**
+ * Loads a new screen image to the currentSkin attribute. This action
+ * updates the skin image that is drawn as skin
+ *
+ * @param imageToSet
+ * The new skin pixel data, as retrieved from the skin plugin
+ * @param changedKey
+ * The key that has changed, if any. If one if provided, only the key area should be redrawn.
+ * Can be <code>null</code>.
+ * @param forceDraw
+ * true if a draw is needed after setting the new image; false if replacing skin image only
+ * will be performed.
+ */
+ private void setSkinImage(ImageData imageToSet, IAndroidKey changedKey, boolean forceDraw)
+ {
+
+ recalculateZoomFactor();
+
+ if (imageToSet != null)
+ {
+ if (currentSkinImage != null)
+ {
+ currentSkinImage.dispose();
+ }
+
+ // Scales the chosen image and sets to currentSkin attribute
+ //
+ // NOTE: width and height cannot be equal to MINIMUM_ZOOM_FACTOR,
+ // because this
+ // will raise an IllegalArgumentException when constructing the
+ // Image object
+ int width =
+ (zoomFactor == MINIMUM_ZOOM_FACTOR ? 1 : (int) (imageToSet.width * zoomFactor));
+ int height =
+ (zoomFactor == MINIMUM_ZOOM_FACTOR ? 1 : (int) (imageToSet.height * zoomFactor));
+ currentSkinImage = new Image(getDisplay(), imageToSet.scaledTo(width, height));
+
+ // It only makes sense to reset the translation if the skin image is really being changed
+ // It will happen if we set a image data without specifying a changed key
+ if (changedKey == null)
+ {
+ displayRectangle.x = 0;
+ displayRectangle.y = 0;
+ }
+
+ if (forceDraw)
+ {
+ layout();
+
+ GC gc = new GC(this);
+ drawSkin(gc, changedKey);
+ gc.dispose();
+ }
+ }
+ else
+ {
+ info("It was requested to set a skin image that was null. Operation aborted.");
+ }
+ }
+
+ /**
+ * This method is responsible to set the scroll bar attributes so that they
+ * reflect the size of the current image at the current zoom factor
+ */
+ private void synchronizeScrollBars()
+ {
+ // Syncronizing only makes sense if there is a skin being drawn
+ if (currentSkinImage != null)
+ {
+
+ // Retrieves the current image and client area sizes
+ Rectangle imageBound = currentSkinImage.getBounds();
+ int cw = getClientArea().width;
+ int ch = getClientArea().height;
+
+ // Updates horizontal scroll bar attributes
+ ScrollBar horizontal = getHorizontalBar();
+ horizontal.setIncrement((cw / 100));
+ horizontal.setPageIncrement((cw / 2));
+ horizontal.setMaximum(imageBound.width);
+ horizontal.setThumb(cw);
+ horizontal.setSelection(displayRectangle.x);
+
+ // Updates vertical scroll bar attributes
+ ScrollBar vertical = getVerticalBar();
+ vertical.setIncrement((ch / 100));
+ vertical.setPageIncrement((ch / 2));
+ vertical.setMaximum(imageBound.height);
+ vertical.setThumb(ch);
+ vertical.setSelection(displayRectangle.y);
+
+ if (horizontal.getMaximum() > cw) // Image is wider than client area
+ {
+ horizontal.setEnabled(true);
+ }
+ else
+ {
+ horizontal.setEnabled(false);
+ }
+
+ if (vertical.getMaximum() > ch) // Image is wider than client area
+ {
+ vertical.setEnabled(true);
+ }
+ else
+ {
+ vertical.setEnabled(false);
+ }
+
+ }
+ }
+
+ /**
+ * Initialize the scroll bars This include: a) setting the initial enabled
+ * state b) adding the necessary listeners
+ */
+ private void initScrollBars()
+ {
+
+ ScrollBar horizontal = getHorizontalBar();
+ horizontal.setEnabled(false);
+ horizontal.addSelectionListener(new SelectionAdapter()
+ {
+ /**
+ * @see org.eclipse.swt.events.SelectionListener#widgetSelected(SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent event)
+ {
+ // Updates the translation
+ displayRectangle.x = ((ScrollBar) event.widget).getSelection();
+
+ // Update the UI
+ layout();
+ redraw();
+ }
+ });
+
+ ScrollBar vertical = getVerticalBar();
+ vertical.setEnabled(false);
+ vertical.addSelectionListener(new SelectionAdapter()
+ {
+ /**
+ * @see org.eclipse.swt.events.SelectionListener#widgetSelected(SelectionEvent)
+ */
+ @Override
+ public void widgetSelected(SelectionEvent event)
+ {
+ // Updates the translation
+ displayRectangle.y = ((ScrollBar) event.widget).getSelection();
+
+ // Update the UI
+ layout();
+ redraw();
+ }
+ });
+
+ debug("Initialized scroll bars");
+ }
+
+ /**
+ * This method retrieves the key that is placed at the given x,y
+ * coordinates, considering the flip status
+ *
+ * @param x
+ * The X coordinate to use at key lookup
+ * @param y
+ * The Y coordinate to use at key lookup
+ *
+ * @return The key placed at the given coordinate, or null if none is found
+ */
+ private IAndroidKey getSkinKey(int x, int y)
+ {
+ IAndroidKey keyToReturn = null;
+ IAndroidEmulatorInstance instance = UIHelper.getInstanceAssociatedToControl(this);
+
+ Collection<IAndroidKey> keyAreas = skin.getKeyDataCollection(instance.getCurrentLayout());
+ if (keyAreas != null)
+ {
+ for (IAndroidKey key : keyAreas)
+ {
+ if (key.isInsideKey(x, y))
+ {
+ if (key instanceof AndroidPressKey)
+ {
+ AndroidPressKey defaultKeyData = (AndroidPressKey) key;
+
+ //if (!defaultKeyData.isFlipSlideValid(instance.isFlipSlideClosed()))
+ if (!defaultKeyData.isFlipSlideValid(false))
+ {
+ continue;
+ }
+
+ }
+
+ keyToReturn = key;
+ break;
+ }
+ }
+ }
+
+ return keyToReturn;
+ }
+
+ /**
+ * Retrieves an image data. If it has never been used at the current
+ * session, loads it from skin. Released image is retrieved if both parameters
+ * are false.
+ *
+ * @param isPressed
+ * true if the image to be retrieved is the pressed image
+ * @param isEnter
+ * true if the image to be retrieved is the enter image. It only has effect if
+ * isPressed == false
+ *
+ * @return An image data containing the desired image pixels
+ */
+ private ImageData getImageData(boolean isPressed, boolean isEnter)
+ {
+
+ ImageData imageData = null;
+
+ IAndroidEmulatorInstance instance = UIHelper.getInstanceAssociatedToControl(this);
+
+ try
+ {
+ if (isPressed)
+ {
+ imageData = skin.getPressedImageData(instance.getCurrentLayout());
+
+ }
+ else
+ {
+ if (isEnter)
+ {
+ imageData = skin.getEnterImageData(instance.getCurrentLayout());
+
+ }
+ else
+ {
+ imageData = skin.getReleasedImageData(instance.getCurrentLayout());
+
+ }
+ }
+ }
+ catch (SkinException e)
+ {
+ error("The image requested from skin could not be retrieved. isPressed=" + isPressed
+ + "; message=" + e.getMessage());
+ EclipseUtils.showErrorDialog(e);
+ error("The skin could not provide an important resource. Stopping the instance");
+ try
+ {
+ instance.stop(true);
+ }
+ catch (InstanceStopException e1)
+ {
+ error("Error while running service for stopping virtual machine");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_General_CannotRunStopService);
+ }
+ }
+
+ return imageData;
+ }
+
+ /**
+ * Builds an image data that is based on the released image but has the
+ * pressed/enter key area painted with data from the pressed/enter image
+ *
+ * @param key
+ * The pressed key
+ * @param isEnter
+ * Whether the image being retrieved will be used for enter or pressed
+ *
+ * @return An image data built the way described at method description
+ */
+ private ImageData getKeyImageData(IAndroidKey key, boolean isEnter)
+ {
+
+ ImageData resultingImage;
+ ImageData releasedImage = getImageData(false, false);
+ ImageData keyImage = isEnter ? getImageData(false, true) : getImageData(true, false);
+
+ resultingImage = (ImageData) releasedImage.clone();
+
+ Rectangle keyArea = key.getKeyArea();
+ int resultingImageSize = resultingImage.width * keyArea.height;
+ int[] keyPixelBuffer = new int[resultingImageSize];
+
+ int startY = keyArea.y < 0 ? 0 : keyArea.y;
+
+ keyImage.getPixels(0, startY, resultingImageSize, keyPixelBuffer, 0);
+
+ for (int line = 0; line < keyArea.height; line++)
+ {
+ int pos = (line * resultingImage.width) + Math.abs(keyArea.x);
+ int startX = Math.abs(keyArea.x);
+ startY = (keyArea.y < 0 ? 0 : keyArea.y) + line;
+ if (startY < 0)
+ {
+ continue;
+ }
+
+ int putWidth = keyArea.x < 0 ? keyArea.width - startX : keyArea.width;
+ resultingImage.setPixels(startX, startY, putWidth, keyPixelBuffer, pos);
+ }
+
+ return resultingImage;
+ }
+
+ /**
+ * This method is called when a mouse selection needs to be canceled.
+ * Pressing the right button, releasing the left button or leaving the key
+ * area are examples of typical conditions.
+ */
+ private void cancelMouseSelection()
+ {
+ // If the mouse timer is different from null, that means that a key is
+ // pressed
+ // This check is important so that event messages are not sent by
+ // mistake
+
+ if (currentKey != null)
+ {
+
+ ImageData newImageData = getImageData(false, true);
+ setSkinImage(newImageData, currentKey, true);
+ androidInput.sendClick(currentKey.getKeysym(), false);
+ }
+ }
+
+ private void changeCurrentKey(IAndroidKey newKey)
+ {
+ // The following actions are executed only if the key has changed since the last
+ // time a mouse move event has happened.
+ ImageData newImage;
+
+ if (currentKey != null)
+ {
+ // If currentKey is different from null, we know that the mouse cursor has
+ // left the area defined by currentKey. That is because from the previous
+ // if clause we know that the key has changed, and currentKey attribute
+ // state is out-dated until we reach the "currentKey = keyData" statement.
+ // In this case, we must draw the RELEASED image version of the key at the
+ // key location
+ newImage = getImageData(false, false);
+ setSkinImage(newImage, currentKey, true);
+ setToolTipText(null);
+ }
+
+ if (newKey != null)
+ {
+ // If keyData is different from null, we know that the mouse cursor has
+ // entered the area defined by keyData.
+ // In this case, we must draw the ENTER image version of the key at the
+ // key location
+ newImage = getKeyImageData(newKey, true);
+ setSkinImage(newImage, newKey, true);
+ setToolTipText(newKey.getToolTip());
+ }
+ currentKey = newKey;
+ }
+
+ /**
+ * Retrieves the display rectangle defined at the current moment.
+ *
+ * @return The display rectangle
+ */
+ Rectangle getDisplayRectangle()
+ {
+ return displayRectangle;
+ }
+
+ /**
+ * Updates the size and location of the display rectangle, based on the
+ * current attributes of the skin (such as skin size and zoom factor) and
+ * view size
+ */
+ void updateDisplayRectangle()
+ {
+ // Updating the display rectangle only makes sense if we have a skin
+ // being drawn
+ if (currentSkinImage != null)
+ {
+
+ // Collect the variables used in computation
+ //
+ // - clientAreaWidth, clientAreaHeight: dimensions of the view,
+ // measured from
+ // the upper left corner point (0,0) to the lower right corner point
+ // (width, height)
+ // - currentSkinWidth, currentSkinHeight: dimensions of the skin
+ // picture, already scaled
+ // by the zoom factor
+ int clientAreaWidth = getClientArea().width;
+ int clientAreaHeight = getClientArea().height;
+ int currentSkinHeight = currentSkinImage.getImageData().height;
+ int currentSkinWidth = currentSkinImage.getImageData().width;
+
+ // Updates the display rectangle y and height
+ //
+ // FIRST STEP: determine the position of the rectangle's y
+ // coordinate.
+ // - It starts by calculating if there is any blank area at the
+ // bottom
+ // of the view.
+ // - If there is blank space (blankY > 0) then calculate which
+ // point,
+ // if set as the display rectangle's y coordinate, would make the
+ // blank
+ // space to disappear. Store this as a candidate Y.
+ // - Check if the candidate Y is valid. If not valid (candidateY <
+ // 0)
+ // then use the image origin as the final y coordinate
+ //
+ // SECOND STEP: determine the width dimension of the rectangle
+ // - It starts by calculating which would be the coordinate of the
+ // lower point, assuming that the image is big enough to contain
+ // that
+ // coordinate. That value (vEdge) is the sum of the Y coordinate and
+ // the view height.
+ // - If vEdge is bigger than the current view height, that means
+ // that the image will occupy part of the view. Itis necessary to
+ // make the rectangle fit in the skin image by making it smaller in
+ // height than the view height itself.
+ // - If vEdge is smaller than the current view height, that means
+ // that
+ // the image will not fit in the view. The solution is to make the
+ // display rectangle height the same size as the view height. In
+ // this
+ // second case, the display rectangle will fit in the view height,
+ // but
+ // will not be bigger than the skin image height itself
+ int blankY = clientAreaHeight - (currentSkinHeight - displayRectangle.y);
+ if (blankY > 0)
+ {
+ int candidateY = displayRectangle.y - blankY;
+ if (candidateY > 0)
+ {
+ displayRectangle.y = candidateY;
+ }
+ else
+ {
+ displayRectangle.y = 0;
+ }
+ }
+ int vEdge = displayRectangle.y + clientAreaHeight;
+ if (vEdge > currentSkinHeight)
+ {
+ displayRectangle.height = currentSkinHeight - displayRectangle.y;
+ }
+ else
+ {
+ displayRectangle.height = clientAreaHeight;
+ }
+
+ // Updates the display rectangle x and width
+ // NOTE: a similar logic to the previous one was applied in this
+ // case
+ int blankX = clientAreaWidth - (currentSkinWidth - displayRectangle.x);
+ if (blankX > 0)
+ {
+ int candidateX = displayRectangle.x - blankX;
+ if (candidateX > 0)
+ {
+ displayRectangle.x = candidateX;
+ }
+ else
+ {
+ displayRectangle.x = 0;
+ }
+ }
+ int hEdge = displayRectangle.x + clientAreaWidth;
+ if (hEdge > currentSkinWidth)
+ {
+ displayRectangle.width = currentSkinWidth - displayRectangle.x;
+ }
+ else
+ {
+ displayRectangle.width = clientAreaWidth;
+ }
+
+ }
+ }
+
+ /**
+ * Recalculates the zoom factor. This is necessary when the screen or image
+ * dimensions change.
+ */
+ void recalculateZoomFactor()
+ {
+
+ if (!scrollBarsUsed)
+ {
+ // Compute new zoom factor if the zoom mode is "Fit to Window"
+ Rectangle clientArea = getClientArea();
+ if ((clientArea.width == 0) || (clientArea.height == 0))
+ {
+ // zoom factor cannot be zero, otherwise an
+ // IllegalArgumentException
+ // is raised in some SWT methods
+ setZoomFactor(MINIMUM_ZOOM_FACTOR);
+ }
+ else
+ {
+ ImageData currentSkin = getImageData(false, false);
+ double widthRatio = (double) (clientArea.width) / currentSkin.width;
+ double heightRatio = (double) (clientArea.height) / currentSkin.height;
+ setZoomFactor(Math.min(widthRatio, heightRatio));
+ }
+ }
+
+ }
+
+ /**
+ * Gets the current zoom factor.
+ *
+ * @return the zoom factor
+ */
+ public double getZoomFactor()
+ {
+ return zoomFactor;
+ }
+
+ /**
+ * Checks if the current zoom configuration is "fit to screen";
+ * @return
+ */
+ public boolean isFitToWindowSelected()
+ {
+ return !scrollBarsUsed;
+ }
+
+ /**
+ * Sets the zoom factor.
+ *
+ * @param zoom
+ * the zoom factor
+ */
+ public void setZoomFactor(double zoom)
+ {
+ zoomFactor = zoom;
+ }
+
+ /**
+ * Gets the listener that handles SWT key pressing and releasing events.
+ *
+ * @return the KeyListener object
+ */
+ public KeyListener getKeyListener()
+ {
+ return keyListener;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.ui.controls.IAndroidComposite#getMouseListener()
+ */
+ public MouseListener getMouseListener()
+ {
+ return mainDisplayMouseListener;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.ui.controls.IAndroidComposite#getMouseMoveListener()
+ */
+ public MouseMoveListener getMouseMoveListener()
+ {
+ return mainDisplayMouseMoveListener;
+ }
+
+ private void createListenersForMainDisplay()
+ {
+ // create listener to handle keyboard key pressing highlighting the key
+ // in the skin.
+ keyListener = new KeyAdapter()
+ {
+
+ @Override
+ public void keyPressed(KeyEvent arg0)
+ {
+
+ int keyCode = arg0.keyCode;
+
+ if (keyCode == SWT.CTRL)
+ {
+ ctrlPressed = true;
+ }
+ else
+ {
+ if (keyCode == SWT.ARROW_DOWN || keyCode == SWT.ARROW_LEFT
+ || keyCode == SWT.ARROW_RIGHT || keyCode == SWT.ARROW_UP)
+ {
+ int dpadRotation = skin.getDpadRotation(androidInstance.getCurrentLayout());
+ keyCode = getRotatedKeyCode(keyCode, dpadRotation);
+ }
+ // send message to emulator
+ androidInput.sendKey(arg0.character, keyCode, skin.getKeyCodes());
+ }
+
+ }
+
+ @Override
+ public void keyReleased(KeyEvent arg0)
+ {
+ int keyCode = arg0.keyCode;
+
+ if (keyCode == SWT.CTRL)
+ {
+ ctrlPressed = false;
+ }
+ }
+
+ };
+
+ mainDisplayMouseMoveListener = new MouseMoveListener()
+ {
+ /**
+ * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(MouseEvent)
+ */
+ public void mouseMove(MouseEvent e)
+ {
+ if (isMouseMainDisplayLeftButtonPressed)
+ {
+ UIHelper.ajustCoordinates(e, SkinComposite.this);
+ androidInput.sendMouseMove((int) (e.x / embeddedViewScale),
+ (int) (e.y / embeddedViewScale));
+ }
+ }
+ };
+
+ mainDisplayMouseListener = new MouseAdapter()
+ {
+ /**
+ * @see org.eclipse.swt.events.MouseListener#mouseUp(MouseEvent)
+ */
+ @Override
+ public void mouseUp(MouseEvent e)
+ {
+ if (e.button == 1)
+ {
+ isMouseMainDisplayLeftButtonPressed = false;
+
+ UIHelper.ajustCoordinates(e, SkinComposite.this);
+ androidInput.sendMouseUp((int) (e.x / embeddedViewScale),
+ (int) (e.y / embeddedViewScale));
+ }
+ }
+
+ /**
+ * @see org.eclipse.swt.events.MouseListener#mouseDown(MouseEvent)
+ */
+ @Override
+ public void mouseDown(MouseEvent e)
+ {
+ if (e.button == 1)
+ {
+ UIHelper.ajustCoordinates(e, SkinComposite.this);
+ androidInput.sendMouseDown((int) (e.x / embeddedViewScale),
+ (int) (e.y / embeddedViewScale));
+
+ isMouseMainDisplayLeftButtonPressed = true;
+ }
+ }
+ };
+ }
+
+ private int getRotatedKeyCode(int keyCode, int dpadRotation)
+ {
+ switch (dpadRotation % 4)
+ {
+ case 1:
+ switch (keyCode)
+ {
+ case SWT.ARROW_DOWN:
+ keyCode = SWT.ARROW_RIGHT;
+ break;
+ case SWT.ARROW_LEFT:
+ keyCode = SWT.ARROW_DOWN;
+ break;
+ case SWT.ARROW_RIGHT:
+ keyCode = SWT.ARROW_UP;
+ break;
+ case SWT.ARROW_UP:
+ keyCode = SWT.ARROW_LEFT;
+ break;
+ }
+ break;
+ case 2:
+ switch (keyCode)
+ {
+ case SWT.ARROW_DOWN:
+ keyCode = SWT.ARROW_UP;
+ break;
+ case SWT.ARROW_LEFT:
+ keyCode = SWT.ARROW_RIGHT;
+ break;
+ case SWT.ARROW_RIGHT:
+ keyCode = SWT.ARROW_LEFT;
+ break;
+ case SWT.ARROW_UP:
+ keyCode = SWT.ARROW_DOWN;
+ break;
+ }
+ break;
+ case 3:
+ switch (keyCode)
+ {
+ case SWT.ARROW_DOWN:
+ keyCode = SWT.ARROW_LEFT;
+ break;
+ case SWT.ARROW_LEFT:
+ keyCode = SWT.ARROW_UP;
+ break;
+ case SWT.ARROW_RIGHT:
+ keyCode = SWT.ARROW_DOWN;
+ break;
+ case SWT.ARROW_UP:
+ keyCode = SWT.ARROW_RIGHT;
+ break;
+ }
+ break;
+ default:
+ //Does nothing, no rotation needed.
+ break;
+ }
+ return keyCode;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/AbstractZoomHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/AbstractZoomHandler.java
new file mode 100644
index 0000000..f6a49a9
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/AbstractZoomHandler.java
@@ -0,0 +1,146 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.handlers;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.Map;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.commands.IElementUpdater;
+import org.eclipse.ui.menus.UIElement;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+import com.motorola.studio.android.emulator.ui.view.AndroidViewData;
+
+/**
+ * DESCRIPTION:
+ * This class is a handler for the zoom actions
+ *
+ * RESPONSIBILITY:
+ * Execute the zoom operations on demand
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by Eclipse only
+ */
+public abstract class AbstractZoomHandler extends AbstractHandler implements IHandlerConstants,
+ IElementUpdater
+{
+ /**
+ * @see org.eclipse.core.commands.IHandler#execute(ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ String viewId = event.getParameter(ACTIVE_VIEW_PARAMETER);
+ double zoomFactor = getZoomFactor(event.getParameters());
+
+ info("Setting zoom factor for " + viewId + " = " + zoomFactor);
+
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ if (instance != null)
+ {
+ IViewPart viewPart = EclipseUtils.getActiveView(viewId);
+ if (viewPart instanceof AbstractAndroidView)
+ {
+ AbstractAndroidView view = (AbstractAndroidView) viewPart;
+ if (view.getZoomFactor(instance) != zoomFactor)
+ {
+ view.setZoomFactor(instance, zoomFactor);
+ view.updateActiveViewer();
+ }
+ }
+ }
+ else
+ {
+ error("The host being presented at the viewer is not available");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_AbstractZoomHandler_InstanceNotFound);
+ }
+ return null;
+ }
+
+ /**
+ * When a different emulator tab have focus, then each of the zoom factor menu items need
+ * to update itself so that the radio selection continues at the correct place
+ *
+ * @see org.eclipse.ui.commands.IElementUpdater#updateElement(UIElement, Map)
+ */
+ @SuppressWarnings("rawtypes")
+ public void updateElement(UIElement element, Map parameters)
+ {
+ boolean checked = false;
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ String viewId = (String) parameters.get(ACTIVE_VIEW_PARAMETER);
+
+ if ((instance != null) && (viewId != null))
+ {
+ IViewPart viewPart = EclipseUtils.getActiveView(viewId);
+ if (viewPart instanceof AbstractAndroidView)
+ {
+ AbstractAndroidView view = (AbstractAndroidView) viewPart;
+ AndroidViewData viewData = view.getViewData(instance);
+
+ double actualZoomFactor = view.getZoomFactor(instance);
+ if (testZoomFactor(viewData, parameters, actualZoomFactor))
+ {
+ checked = true;
+ }
+ }
+ }
+
+ element.setChecked(checked);
+ }
+
+ /**
+ * The zoom actions are enabled when a phone skin is being presented only
+ *
+ * @see org.eclipse.core.commands.IHandler#isEnabled()
+ */
+ @Override
+ public boolean isEnabled()
+ {
+ return AbstractAndroidView.getActiveInstance() != null;
+ }
+
+ /**
+ * Retrieves the zoom factor associated to this zoom handler instance
+ *
+ * @return The zoom factor, as defined in IEmulatorActionConstants class
+ */
+ @SuppressWarnings("rawtypes")
+ protected abstract double getZoomFactor(Map paramters);
+
+ /**
+ * Tests if the current zoom factor is the one handled by this zoom handler
+ *
+ * @param zoomFactor The active instance current zoom factor
+ *
+ * @return True if this handler handles the current zoom factor; false otherwise
+ */
+ @SuppressWarnings("rawtypes")
+ protected abstract boolean testZoomFactor(AndroidViewData viewData, Map parameters,
+ double zoomFactor);
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ChangeEmulatorOrientationHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ChangeEmulatorOrientationHandler.java
new file mode 100644
index 0000000..6f6af4b
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ChangeEmulatorOrientationHandler.java
@@ -0,0 +1,119 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.ui.IViewPart;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+import com.motorola.studio.android.emulator.ui.view.AndroidView;
+import com.motorola.studio.android.nativeos.IDevicePropertiesOSConstants;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * This class executes the operation of setting an Emulator Layout with the Landscape orientation
+ **/
+public class ChangeEmulatorOrientationHandler extends AbstractHandler
+{
+
+ /**
+ * Sets a layout with the Landscape orientation for the active Emulator.
+ */
+ public Object execute(ExecutionEvent event)
+ {
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+
+ if (instance != null)
+ {
+ String viewId = event.getParameter(IHandlerConstants.ACTIVE_VIEW_PARAMETER);
+ if ((instance != null) && (viewId != null))
+ {
+ IViewPart viewPart = EclipseUtils.getActiveView(viewId);
+ if (viewPart instanceof AndroidView)
+ {
+ if ((instance.getProperties().getProperty(IDevicePropertiesOSConstants.useVnc,
+ NativeUIUtils.getDefaultUseVnc())).equals("true"))
+ {
+ String nextLayout =
+ AbstractAndroidView.getPreviousOrNextLayout(viewId,
+ AbstractAndroidView.LayoutOpp.NEXT);
+ AbstractAndroidView.changeLayout(nextLayout);
+
+ IAndroidSkin skin = ((AbstractAndroidView) viewPart).getSkin(instance);
+ String cmd = skin.getLayoutScreenCommand(instance.getCurrentLayout());
+ instance.changeOrientation(cmd);
+ }
+ else
+ {
+ AndroidView androidView = (AndroidView) viewPart;
+ androidView.changeToNextLayout();
+ }
+ }
+ }
+
+ }
+
+ return null;
+ }
+
+ /**
+ * Determines when the Landscape command can be executed.
+ */
+ @Override
+ public boolean isEnabled()
+ {
+ return AbstractAndroidView.getActiveInstance() != null;
+ }
+
+ // /**
+ // * Determines what layout is the current one and checks it
+ // */
+ // @SuppressWarnings("unchecked")
+ // public void updateElement(UIElement element, Map parameters)
+ // {
+ // boolean checked = false;
+ //
+ // IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ // String viewId = (String) parameters.get(IHandlerConstants.ACTIVE_VIEW_PARAMETER);
+ //
+ // if ((instance != null) && (viewId != null))
+ // {
+ // IViewPart viewPart = EclipseUtils.getActiveView(viewId);
+ // if (viewPart instanceof AbstractAndroidView)
+ // {
+ // AbstractAndroidView view = (AbstractAndroidView) viewPart;
+ // IAndroidSkin skin = view.getSkin(instance);
+ //
+ // if (skin != null)
+ // {
+ // String currentLayout = instance.getCurrentLayout();
+ // String setLayoutName =
+ // (String) parameters.get(IHandlerConstants.LAYOUT_TO_SET_PARAMETER);
+ // if ((setLayoutName != null) && (setLayoutName.equals(currentLayout)))
+ // {
+ // checked = true;
+ // }
+ // }
+ // }
+ // }
+ //
+ // element.setChecked(checked);
+ // }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ChangeZoomHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ChangeZoomHandler.java
new file mode 100644
index 0000000..e71c062
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ChangeZoomHandler.java
@@ -0,0 +1,80 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.handlers;
+
+import java.util.Map;
+
+import com.motorola.studio.android.emulator.ui.controls.IAndroidComposite;
+import com.motorola.studio.android.emulator.ui.view.AndroidViewData;
+
+public class ChangeZoomHandler extends AbstractZoomHandler
+{
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.ui.handlers.AbstractZoomHandler#getZoomFactor(java.util.Map)
+ */
+ @SuppressWarnings("rawtypes")
+ @Override
+ protected double getZoomFactor(Map parameter)
+ {
+ double zoomFactor = DEFAULT_ZOOM;
+ String factorString = (String) parameter.get(ZOOM_FACTOR_PARAMETER);
+ try
+ {
+ zoomFactor = Double.parseDouble(factorString);
+ }
+ catch (Exception e)
+ {
+ // Do nothing
+ // The parameter can always be parsed
+ }
+ return zoomFactor;
+ }
+
+ /**
+ * Tests if the current zoom factor is the one handled by this zoom handler
+ *
+ * @param zoomFactor The active instance current zoom factor
+ *
+ * @return True if this handler handles the current zoom factor; false otherwise
+ */
+ @SuppressWarnings("rawtypes")
+ @Override
+ protected boolean testZoomFactor(AndroidViewData viewData, Map parameters, double zoomFactor)
+ {
+ boolean testResult = false;
+ double expectedZoomFactor = getZoomFactor(parameters);
+ if (expectedZoomFactor == zoomFactor)
+ {
+ testResult = true;
+ }
+
+ if (expectedZoomFactor == ZOOM_FIT)
+ {
+ if (viewData != null)
+ {
+ IAndroidComposite composite = viewData.getComposite();
+
+ if (composite.isFitToWindowSelected())
+ {
+ testResult = true;
+ }
+ }
+ }
+
+ return testResult;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/IHandlerConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/IHandlerConstants.java
new file mode 100644
index 0000000..9a414fc
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/IHandlerConstants.java
@@ -0,0 +1,69 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.handlers;
+
+/**
+ * This interface contain constants that are used by the action handlers
+ */
+public interface IHandlerConstants
+{
+ /**
+ * Constant used in plugin.xml file to identify change orientation command
+ */
+ String CHANGE_EMULATOR_ORIENTATION_COMMAND =
+ "com.motorola.studio.android.emulator.ui.change.layout";
+
+ /**
+ * Constant used in plugin.xml file to identify change zoom command
+ */
+ String CHANGE_EMULATOR_ZOOM_COMMAND = "com.motorola.studio.android.emulator.ui.change.zoom";
+
+ /**
+ * Parameter that determines to which view the command will be applied
+ */
+ String ACTIVE_VIEW_PARAMETER = "activeViewId";
+
+ /**
+ * Parameter to determines the zoom fact to be set in the
+ */
+ String ZOOM_FACTOR_PARAMETER = "zoomFactor";
+
+ /**
+ * Parameter to determine the increment/decrement to be applied in the current zoonFactor
+ */
+ String ZOOM_CHANGE_FACTOR_PARAMETER = "zoomChangeFactor";
+
+ /**
+ * Parameter to determine the emulator display orientation (next, previous, setlayout) to be set.
+ */
+ String EMULATOR_ORIENTATION_PARAMETER = "emulatorOrientation";
+
+ /**
+ * Parameter to determine the layout to be set, if EMULATOR_ORIENTATION_PARAMETER value is setlayout
+ */
+ String LAYOUT_TO_SET_PARAMETER = "layoutToSet";
+
+ // Zoom constants
+ double ZOOM_FIT = 0;
+
+ double MINIMUM_ZOOM = 0.25;
+
+ double MAXIMUM_ZOOM = 2.0;
+
+ double DEFAULT_ZOOM = 1.00;
+
+ double STEP_ZOOM = 0.25;
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ShowViewHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ShowViewHandler.java
new file mode 100644
index 0000000..a2a15e2
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ShowViewHandler.java
@@ -0,0 +1,79 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.handlers;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.ui.PartInitException;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.ui.view.AndroidView;
+import com.motorola.studio.android.emulator.ui.view.MainDisplayView;
+
+/**
+ * DESCRIPTION:
+ * This class is a handler for the show view actions
+ *
+ * RESPONSIBILITY:
+ * Execute the show view operations on demand
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * This class is intended to be used by Eclipse only
+ */
+public class ShowViewHandler extends AbstractHandler implements IHandlerConstants
+{
+ /**
+ * @see org.eclipse.core.commands.IHandler#execute(ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException
+ {
+ String viewId = event.getParameter(IHandlerConstants.ACTIVE_VIEW_PARAMETER);
+
+ try
+ {
+ if (viewId.equals(AndroidView.ANDROID_VIEW_ID))
+ {
+ info("Showing Main Display View by command execution");
+ EclipseUtils.showView(MainDisplayView.EMULATOR_MAIN_DISPLAY_VIEW_ID);
+ }
+ else if (viewId.equals(MainDisplayView.EMULATOR_MAIN_DISPLAY_VIEW_ID))
+ {
+ info("Showing Android View by command execution");
+ EclipseUtils.showView(AndroidView.ANDROID_VIEW_ID);
+ }
+ else
+ {
+ info("User tried to open an unknown view. Ignoring the action.");
+ }
+ }
+ catch (PartInitException e)
+ {
+ error("The views that were requested to be opened are not accessible programatically");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_AndroidView_ViewNotFound);
+ }
+
+ return null;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ZoomInOutHandler.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ZoomInOutHandler.java
new file mode 100644
index 0000000..a0d2df4
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/handlers/ZoomInOutHandler.java
@@ -0,0 +1,90 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.handlers;
+
+import java.util.Map;
+
+import org.eclipse.ui.IViewPart;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.ui.view.AbstractAndroidView;
+import com.motorola.studio.android.emulator.ui.view.AndroidViewData;
+
+/**
+ * This class is responsible for increasing the default zoom factor
+ * to the current viewer of the Main Display View.
+ */
+public class ZoomInOutHandler extends AbstractZoomHandler
+{
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.ui.handlers.AbstractZoomHandler#getZoomFactor(java.util.Map)
+ */
+ @SuppressWarnings("rawtypes")
+ @Override
+ protected double getZoomFactor(Map parameters)
+ {
+ double zoomFactor = DEFAULT_ZOOM;
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ String viewId = (String) parameters.get(ACTIVE_VIEW_PARAMETER);
+ String changeFactorString = (String) parameters.get(ZOOM_CHANGE_FACTOR_PARAMETER);
+
+ if ((instance != null) && (viewId != null) && (changeFactorString != null))
+ {
+ IViewPart viewPart = EclipseUtils.getActiveView(viewId);
+ if (viewPart instanceof AbstractAndroidView)
+ {
+ AbstractAndroidView view = (AbstractAndroidView) viewPart;
+ double currentZoomFactor = view.getZoomFactor(instance);
+
+ try
+ {
+ double changeZoomFactor = Double.parseDouble(changeFactorString);
+ zoomFactor = currentZoomFactor + changeZoomFactor;
+ }
+ catch (Exception e)
+ {
+ zoomFactor = currentZoomFactor;
+ }
+ }
+ }
+
+ if (zoomFactor < MINIMUM_ZOOM)
+ {
+ zoomFactor = MINIMUM_ZOOM;
+ }
+ else if (zoomFactor > MAXIMUM_ZOOM)
+ {
+ zoomFactor = MAXIMUM_ZOOM;
+ }
+
+ return zoomFactor;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.motorola.studio.android.emulator.ui.handlers.AbstractZoomHandler#testZoomFactor(com.motorola.studio.android.emulator.ui.view.AndroidViewData, java.util.Map, double)
+ */
+ @SuppressWarnings("rawtypes")
+ @Override
+ protected boolean testZoomFactor(AndroidViewData viewData, Map parameters, double zoomFactor)
+ {
+ // It does not make sense to set as checked any of the UI Elements that use the zoom in/out command
+ // Those elements do not represent states, but actions.
+ return false;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/AndroidEmulatorPerspective.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/AndroidEmulatorPerspective.java
new file mode 100644
index 0000000..e1ba398
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/AndroidEmulatorPerspective.java
@@ -0,0 +1,254 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.perspective;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.util.Collection;
+import java.util.TreeSet;
+
+import org.eclipse.ui.IFolderLayout;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+import org.eclipse.ui.PartInitException;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.ui.perspective.extension.AndroidPerspectiveExtensionBean;
+import com.motorola.studio.android.emulator.ui.perspective.extension.AndroidPerspectiveExtensionBean.PerspectiveAreas;
+import com.motorola.studio.android.emulator.ui.perspective.extension.AndroidPerspectiveExtensionReader;
+import com.motorola.studio.android.emulator.ui.perspective.extension.IAndroidPerspectiveExtensionConstants;
+import com.motorola.studio.android.emulator.ui.view.AndroidView;
+import com.motorola.studio.android.emulator.ui.view.MainDisplayView;
+
+/**
+ * DESCRIPTION:
+ * This class represents the Android Emulator perspective.
+ *
+ * RESPONSIBILITY:
+ * Create the Android Emulator perspective.
+ *
+ * COLABORATORS:
+ * None
+ *
+ * USAGE:
+ * This class is referenced by the plugin.xml file of this plugin.
+ */
+public class AndroidEmulatorPerspective implements IPerspectiveFactory
+{
+ private static String LAUNCH_COOLBAR_SHORTCUT = "org.eclipse.debug.ui.launchActionSet";
+
+ /**
+ * Creates the initial layout for a page.
+ *
+ * @param layout the page layout
+ *
+ * @see IPerspectiveFactory#createInitialLayout(IPageLayout)
+ */
+ public void createInitialLayout(IPageLayout layout)
+ {
+ // ---------------HOW THE PERSPECTIVE IS LAID OUT---------------
+ //
+ // The Android Perspective will be dynamically populated according to
+ // the contributions declared to it through the androidPerspectiveExtension
+ // extension point.
+ // The perspective consists of three areas:
+ // - the area where the Android Emulator View is placed, on the right side
+ // - the area where device management related views are placed, on the left side
+ // - the area for emulation views, on the middle
+ // The areas that are dynamically populated are the two last ones, and they will
+ // be referred to as 'dynamic areas' by methods.
+ //
+ // The first area is filled by the code on this method by itself, and the two other
+ // ones are filled depending on what is found for the extension point.
+ // The following drawing illustrates the position of each area on the workbench:
+ //
+ // ____________________________________
+ // | | | |
+ // | | | |
+ // | Dev | | Moto |
+ // | Mgt | | magx |
+ // | Views | Emu Views Area | Emu |
+ // | Area | | View |
+ // | | | Area |
+ // | | | |
+ // |_______|___________________|_______|
+ //
+ // Device Management views are placed atop of each other on their respective area.
+ // Emulation views are laid out depending on the number of views declared. If there
+ // is only one emulation view, it is placed occupying the entire emulation views area.
+ // If there are two emulation views, they divide the emulation views area in half and
+ // occupy the area from bottom to top, as seen on the following drawing:
+ //
+ // ____________________________________
+ // | | | |
+ // | | | |
+ // | Dev | Emu Views part 2 | Moto |
+ // | Mgt | | magx |
+ // | Views |___________________| Emu |
+ // | Area | | View |
+ // | | Emu Views part 1 | Area |
+ // | | | |
+ // |_______|___________________|_______|
+ //
+ // If there are three emulation views or more, they divide the emulation views area in
+ // three parts, which are occupied from bottom to top, as seen on the following drawing:
+ //
+ // ____________________________________
+ // | | | |
+ // | | Emu Views part 3 | |
+ // | Dev |___________________| Moto |
+ // | Mgt | | magx |
+ // | Views | Emu Views part 2 | Emu |
+ // | Area |___________________| View |
+ // | | | Area |
+ // | | Emu Views part 1 | |
+ // |_______|___________________|_______|
+ //
+ // If there are no device management or no emulation views (or both), their folders are
+ // always created so that the workbench is always divided into the correct areas for
+ // better user experience.
+ //
+ // -------------------------------------------------------------
+
+ addEmulatorView(layout);
+ addRunCoolbar(layout);
+ createAndPopulateDynamicAreas(layout);
+
+ // hide the editor area (not necessary on this perspective)
+ layout.setEditorAreaVisible(false);
+ }
+
+ private void addEmulatorView(IPageLayout layout)
+ {
+ // emulator view is a sticky view, no place holder is necessary (only open it)
+ try
+ {
+ EclipseUtils.showView(AndroidView.ANDROID_VIEW_ID);
+ }
+ catch (PartInitException e)
+ {
+ error("Unable to open Android Emulator View on Android Emulator Perspective.");
+ }
+
+ layout.addShowViewShortcut(AndroidView.ANDROID_VIEW_ID);
+ layout.addShowViewShortcut(MainDisplayView.EMULATOR_MAIN_DISPLAY_VIEW_ID);
+ }
+
+ private void addRunCoolbar(IPageLayout layout)
+ {
+ layout.addActionSet(LAUNCH_COOLBAR_SHORTCUT);
+ }
+
+ private void createAndPopulateDynamicAreas(IPageLayout layout)
+ {
+ // read the extensions for this perspective, as declared by the androidPerspectiveExtension
+ // extension point
+ Collection<AndroidPerspectiveExtensionBean> perspectiveExtensionBeans =
+ AndroidPerspectiveExtensionReader.readAndroidPerspectiveExtensions();
+
+ // the folder for placing device management related views
+ IFolderLayout devMgtArea = createDeviceMgtViewsArea(layout);
+
+ // collection of emuy area view ids for later placement
+ // it is alphabetically ordered (so that a group of defined
+ // views always open on the same location)
+ Collection<String> emuAreaViewIds = new TreeSet<String>();
+
+ for (AndroidPerspectiveExtensionBean extensionBean : perspectiveExtensionBeans)
+ {
+ if (PerspectiveAreas.DEVICE_MANAGEMENT_VIEWS_AREA.equals(extensionBean.getArea()))
+ {
+ // put dev mgt views atop of each other on the folder
+ devMgtArea.addView(extensionBean.getViewId());
+ }
+ else if (PerspectiveAreas.EMULATION_VIEWS_AREA.equals(extensionBean.getArea()))
+ {
+ // collect emu views for later placement
+ emuAreaViewIds.add(extensionBean.getViewId());
+ }
+ else
+ {
+ // in case of something not expected, log the problem
+ warn("View of id " + extensionBean.getViewId()
+ + " could not be added to Android Emulator perspective");
+ }
+ }
+
+ // the number of emu views, for defining the number of folders necessary
+ int numEmuViews = emuAreaViewIds.size();
+
+ // create the emu area folders, which will be at most size 3
+ IFolderLayout[] emuAreaFolders = createEmuArea(layout, numEmuViews);
+
+ // place the views on the correct folder by using the leftover of dividing the
+ // number of the current view by number of maximum folders (3)
+ int i = 0;
+ for (String viewId : emuAreaViewIds)
+ {
+ emuAreaFolders[i % 3].addView(viewId);
+ i++; // next view
+ }
+ }
+
+ private IFolderLayout createDeviceMgtViewsArea(IPageLayout layout)
+ {
+ return layout.createFolder(IAndroidPerspectiveExtensionConstants.ATT_AREA_DEVMGT_VALUE,
+ IPageLayout.LEFT, 0.30f, IPageLayout.ID_EDITOR_AREA);
+ }
+
+ private IFolderLayout[] createEmuArea(IPageLayout layout, int numEmuViews)
+ {
+ // at most 3 folders are necessary
+ IFolderLayout[] emuAreaFolders = new IFolderLayout[3];
+
+ // the number of divisions the emu views area will be divided into
+ int div = (numEmuViews >= 3 ? 3 : (numEmuViews == 2 ? 2 : 1));
+
+ // the ratio for the first folder to be placed (the bottom one, which may turn
+ // to be the only one if div is equal to 1)
+ float ratio = 1.00f / div;
+
+ // the bottom folder (#1) is always necessary
+ // for 3 folders, ratio = 0.33f; for 2 folders, ratio = 0.5f
+ emuAreaFolders[0] =
+ layout.createFolder("emuAreaBottom", IPageLayout.BOTTOM, ratio,
+ IPageLayout.ID_EDITOR_AREA);
+
+ // create folder #2 only if necessary
+ if (numEmuViews >= 2)
+ {
+ // adjust ratio depending on the number of folders in total:
+ // for 3 folders, ratio = 0.67f; for 2 folders, ratio = 0.5f
+ if (numEmuViews > 2)
+ {
+ ratio = 2 * ratio;
+ }
+ emuAreaFolders[1] =
+ layout.createFolder("emuAreaMiddle", IPageLayout.TOP, ratio, "emuAreaBottom");
+ }
+
+ // create folder #3 only if necessary
+ if (numEmuViews >= 3)
+ {
+ // ratio is always half of folder #2 space
+ emuAreaFolders[2] =
+ layout.createFolder("emuAreaTop", IPageLayout.TOP, 0.50f, "emuAreaMiddle");
+ }
+
+ return emuAreaFolders;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/AndroidPerspectiveExtensionBean.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/AndroidPerspectiveExtensionBean.java
new file mode 100644
index 0000000..cb109c1
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/AndroidPerspectiveExtensionBean.java
@@ -0,0 +1,98 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.perspective.extension;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class is a bean carrying information on a particular declaration of the
+ * androidPerspectiveExtension extension point.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * Carry information about a declaration of androidPerspectiveExtension.
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * None
+ * <br>
+ * USAGE:
+ * <br>
+ * This class should be instantiated only by the reader of the extension points.
+ * Clients may only get the values stored through the getter methods.
+ */
+public class AndroidPerspectiveExtensionBean
+{
+ private String viewId;
+
+ private PerspectiveAreas area;
+
+ /**
+ * Creates a new AndroidPerspectiveExtensionBean object with the given information.
+ *
+ * @param viewId the id of the view
+ * @param area the area to which it should be placed
+ */
+ AndroidPerspectiveExtensionBean(String viewId, String area)
+ {
+ this.viewId = viewId;
+
+ if (IAndroidPerspectiveExtensionConstants.ATT_AREA_DEVMGT_VALUE.equals(area))
+ {
+ this.area = PerspectiveAreas.DEVICE_MANAGEMENT_VIEWS_AREA;
+ }
+ else if (IAndroidPerspectiveExtensionConstants.ATT_AREA_EMU_VALUE.equals(area))
+ {
+ this.area = PerspectiveAreas.EMULATION_VIEWS_AREA;
+ }
+ else
+ {
+ this.area = PerspectiveAreas.UNKNOWN_AREA;
+ }
+ }
+
+ /**
+ * Retrieves the id of the view.
+ *
+ * @return the id of the view
+ */
+ public String getViewId()
+ {
+ return viewId;
+ }
+
+ /**
+ * Retrieves the area to which the view should be added.
+ *
+ * @return the area on the perspective
+ */
+ public PerspectiveAreas getArea()
+ {
+ return area;
+ }
+
+ /**
+ *
+ * This enum represents the available areas to which a view can be added to the Android
+ * Emulator Perspective.
+ * It has a value for UNKNOWN_AREA for robustness purpose.
+ *
+ */
+ public static enum PerspectiveAreas
+ {
+ DEVICE_MANAGEMENT_VIEWS_AREA, EMULATION_VIEWS_AREA, UNKNOWN_AREA
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/AndroidPerspectiveExtensionReader.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/AndroidPerspectiveExtensionReader.java
new file mode 100644
index 0000000..873f2f9
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/AndroidPerspectiveExtensionReader.java
@@ -0,0 +1,90 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.perspective.extension;
+
+import static com.motorola.studio.android.common.log.StudioLogger.warn;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+
+/**
+ * DESCRIPTION:
+ * <br>
+ * This class is the reader for the androidPerspectiveExtension extension point declarations.
+ * <br>
+ * RESPONSIBILITY:
+ * <br>
+ * Retrieve information from plugins declaring the androidPerspectiveExtension.
+ * <br>
+ * COLABORATORS:
+ * <br>
+ * IAndroidPerspectiveExtensionConstants: implements this interface for using the constants directly
+ * <br>
+ * USAGE:
+ * <br>
+ * This class methods should be called statically from within the implementation of the Android
+ * Emulator Perspective.
+ */
+public class AndroidPerspectiveExtensionReader implements IAndroidPerspectiveExtensionConstants
+{
+
+ /**
+ * Reads the information from plug-ins declaring extensions to androidPerspectiveExtension
+ * and stores it into beans.
+ *
+ * @return a collection of beans containing androidPerspectiveExtension information
+ */
+ public static Collection<AndroidPerspectiveExtensionBean> readAndroidPerspectiveExtensions()
+ {
+ Collection<AndroidPerspectiveExtensionBean> beans =
+ new LinkedHashSet<AndroidPerspectiveExtensionBean>();
+
+ IExtension[] extensions = EclipseUtils.getInstalledPlugins(EXTENSION_POINT_ID);
+
+ for (IExtension extension : extensions)
+ {
+ IConfigurationElement[] viewElements = extension.getConfigurationElements();
+
+ for (IConfigurationElement viewElement : viewElements)
+ {
+ if (ELEMENT_VIEW.equals(viewElement.getName()))
+ {
+ String viewId = viewElement.getAttribute(ATT_ID);
+ String area = viewElement.getAttribute(ATT_AREA);
+
+ // create the bean only if information could be read
+ if ((viewId != null) && (!viewId.equals("")) && (area != null)
+ && (!area.equals("")))
+ {
+ beans.add(new AndroidPerspectiveExtensionBean(viewId, area));
+ }
+ else
+ {
+ warn("View not added to Android Emulator Perspective area for extension "
+ + extension.getExtensionPointUniqueIdentifier());
+ }
+ }
+ }
+ }
+
+ return beans;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/IAndroidPerspectiveExtensionConstants.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/IAndroidPerspectiveExtensionConstants.java
new file mode 100644
index 0000000..3d1424b
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/perspective/extension/IAndroidPerspectiveExtensionConstants.java
@@ -0,0 +1,39 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.perspective.extension;
+
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+
+/**
+ *
+ * This interface provides the String values used by the androidPerspectiveExtension
+ * extension point for purpose of reading the declared extensions.
+ *
+ */
+public interface IAndroidPerspectiveExtensionConstants
+{
+ String EXTENSION_POINT_ID = EmulatorPlugin.PLUGIN_ID + ".androidPerspectiveExtension";
+
+ String ELEMENT_VIEW = "view";
+
+ String ATT_ID = "id";
+
+ String ATT_AREA = "area";
+
+ String ATT_AREA_DEVMGT_VALUE = "devicemanagementviews";
+
+ String ATT_AREA_EMU_VALUE = "emulationviews";
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AbstractAndroidView.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AbstractAndroidView.java
new file mode 100644
index 0000000..cf6cb0f
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AbstractAndroidView.java
@@ -0,0 +1,1839 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.view;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+import static com.motorola.studio.android.common.log.StudioLogger.info;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.sequoyah.device.framework.model.IInstance;
+import org.eclipse.sequoyah.vnc.protocol.PluginProtocolActionDelegate;
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolMessage;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.IRemoteDisplay;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.FocusAdapter;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IPerspectiveDescriptor;
+import org.eclipse.ui.IPerspectiveListener;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchListener;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.part.ViewPart;
+
+import com.motorola.studio.android.common.preferences.DialogWithToggleUtils;
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.common.utilities.PluginUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.exception.StartCancelledException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.model.IEmulatorView;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic.LogicMode;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+import com.motorola.studio.android.emulator.logic.StartVncServerLogic;
+import com.motorola.studio.android.emulator.logic.stop.AndroidEmulatorStopper;
+import com.motorola.studio.android.emulator.ui.IUIHelpConstants;
+import com.motorola.studio.android.emulator.ui.controls.IAndroidComposite;
+import com.motorola.studio.android.emulator.ui.controls.RemoteCLIDisplay;
+import com.motorola.studio.android.emulator.ui.controls.nativewindow.NativeWindowComposite;
+import com.motorola.studio.android.nativeos.IDevicePropertiesOSConstants;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * DESCRIPTION:
+ * This class represents the Android Emulator view. It provides the
+ * generic methods of the Emulator Views. The specific ones must be defined
+ * by the classes that extend it.
+ *
+ * RESPONSIBILITY:
+ * - Show the viewers to the end user
+ *
+ * COLABORATORS:
+ * None.
+ *
+ * USAGE:
+ * The public interface provides static and dynamic methods:
+ * STATIC METHODS:
+ * - Call getActiveInstance to retrieve the instance that corresponds to
+ * the emulator running at the active tab
+ * - Call updateActiveViewers to guarantee that the active viewer of all views
+ * is up to date in all emulator views opened, but do not make further verifications.
+ * DYNAMIC METHODS:
+ * - Call refreshView to updates all viewers including creation of viewers
+ * for started instances and removal of viewers
+ * - Call updateActiveViewer to guarantee that the active viewer is up to
+ * date in all emulator views opened, but do not make further verifications
+ */
+public abstract class AbstractAndroidView extends ViewPart implements IEmulatorView
+{
+ private final MenuManager menuManager;
+
+ public static final String POPUP_MENU_ID = "com.motorola.studio.android.emulator.view.popup";
+
+ private MouseListener mouseClickListener;
+
+ /**
+ * Preference key of the Question Dialog about stopping the emulators by closing view
+ */
+ private static String STOP_BY_CLOSING_VIEW_KEY_PREFERENCE = "stop.by.closing.view";
+
+ /**
+ * Preference key of the Question Dialog about displaying all emulators in the IDE
+ *
+ */
+ private static String SHOW_EMULATOR_IN_THE_IDE_KEY_PREFERENCE =
+ "show.view.for.started.emulators";
+
+ /**
+ * Preference key of the Question Dialog about stopping all emulators in shutdown
+ *
+ */
+ private static String STOP_ALL_EMULATORS_IN_SHUTDOWN_KEY_PREFERENCE =
+ "stop.all.emulators.in.shutdown";
+
+ /**
+ * All event types handled by the listeners in this class
+ */
+ public static final int[] SWT_EVENT_TYPES = new int[]
+ {
+ SWT.KeyDown, SWT.KeyUp, SWT.MouseDown, SWT.MouseUp, SWT.MouseMove, SWT.MouseDoubleClick
+ };
+
+ /**
+ * All possible Layout Operations
+ */
+ public enum LayoutOpp
+ {
+ KEEP, NEXT
+ };
+
+ /**
+ * Tab folder where to place each instance tab
+ */
+ private TabFolder tabFolder;
+
+ Listener listener = new Listener()
+ {
+ public void handleEvent(Event event)
+ {
+ if (tabFolder.getItemCount() > 0)
+ {
+ TabItem activeTabItem = getActiveTabItem();
+
+ if ((activeTabItem != null) && (activeTabItem.getControl() != null))
+ {
+ info("Setting focus to Android Emulator " + activeTabItem.getData());
+ activeTabItem.getControl().setFocus();
+ }
+ }
+ }
+ };
+
+ /**
+ * Map to collect the emulator AndroidViewData committed to its emulator instance.
+ */
+ private final Map<IAndroidEmulatorInstance, AndroidViewData> instanceDataMap =
+ new LinkedHashMap<IAndroidEmulatorInstance, AndroidViewData>();
+
+ /**
+ * Listener required to code the work-around for Sticky Views on perspectiveChanged method.
+ */
+ private PerspectiveListenerImpl perspectiveListenerImpl;
+
+ /**
+ * Listener necessary to determine when the view is closed.
+ */
+ private PartListenerImpl partListenerImpl;
+
+ /**
+ * Listener used to know if the view is being closed due to workbench shutdown.
+ */
+ private static WorkbenchListenerImpl workbenchListenerImpl;
+
+ // the view is being closed during the Studio shutdown.
+ private boolean closingOnShutdown = false;
+
+ // Collection of the ids of the opened views that overwrite this class
+ private static final List<String> childrenIDs = new ArrayList<String>();
+
+ // the instance being currently active at the emulator views
+ private static IAndroidEmulatorInstance activeInstance;
+
+ /**
+ * Listeners of tab switch event
+ */
+ private static final Collection<Listener> tabSwitchListeners = new ArrayList<Listener>();
+
+ /**
+ * Lock to assure that only the first thread will display the show view question
+ */
+ private static Lock showViewLock = new ReentrantReadWriteLock().writeLock();
+
+ /**
+ * Add a listener to be called when the tab selection changes
+ *
+ * @param listener the listener to be added
+ */
+ public static void addTabSwitchListener(Listener listener)
+ {
+ tabSwitchListeners.add(listener);
+ }
+
+ /**
+ * Remove a listener that listen to tab switch events
+ *
+ * @param listener the listener to be removed
+ */
+ public static void removeTabSwitchListener(Listener listener)
+ {
+ tabSwitchListeners.remove(listener);
+ }
+
+ /**
+ * Call listeners of tab switch events
+ */
+ protected void handleTabSwitchEvent()
+ {
+ for (Listener listener : tabSwitchListeners)
+ {
+ listener.handleEvent(null);
+ }
+ }
+
+ /**
+ * Returns the View Identification.
+ * @return the unique ViewId
+ */
+ protected abstract String getViewId();
+
+ /**
+ * Creates the graphical elements representing the emulator that will be
+ * shown by the viewer in its tab item.
+ *
+ * @param tab the tab item that will hold the graphical elements that
+ * represents the emulator
+ * @param instance the emulator instance
+ * @param emulatorData the object to be defined with the elements created.
+ * @throws SkinException if the AVD configured skin does not exists
+ */
+ protected abstract void createWidgets(TabItem tab, final IAndroidEmulatorInstance instance,
+ final AndroidViewData emulatorData) throws SkinException;
+
+ /**
+ * Forces the refreshing of the menu elements.
+ */
+ protected abstract void refreshMenuElements();
+
+ /**
+ * Retrieves the instance being currently active at the emulator views.
+ *
+ * @return The active instance, or null if there is no active instance
+ */
+ public static IAndroidEmulatorInstance getActiveInstance()
+ {
+ return activeInstance;
+ }
+
+ /**
+ * Retrieves the instance being currently active at the emulator views.
+ *
+ * @return The active instance, or null if there is no active instance
+ */
+ public static void setInstance(IAndroidEmulatorInstance emulatorInstance)
+ {
+ if (!childrenIDs.isEmpty())
+ {
+ AbstractAndroidView view =
+ (AbstractAndroidView) EclipseUtils.getActiveView(childrenIDs.get(0));
+ if (view != null)
+ {
+ view.setActiveInstanceId(emulatorInstance.getInstanceIdentifier());
+ activeInstance = emulatorInstance;
+ }
+ }
+ }
+
+ /**
+ * Retrieves the information about the viewer used to display the given emulator instance
+ * and returns null if there is no AndroidViewData for the given emulator instance.
+ * viewer.
+ * @param the emulator instance whose Android Viewer data need to be retrieved.
+ * @return the AndroidViewerData for the given Emulator instance (null if none is available).
+ */
+ public AndroidViewData getViewData(IAndroidEmulatorInstance instance)
+ {
+ AndroidViewData viewData = instanceDataMap.get(instance);
+ return viewData;
+ }
+
+ public IAndroidSkin getSkin(IAndroidEmulatorInstance instance)
+ {
+ IAndroidSkin skin = null;
+ AndroidViewData viewData = getViewData(instance);
+ if (viewData != null)
+ {
+ skin = viewData.getSkin();
+ }
+ return skin;
+ }
+
+ /**
+ * Gets the layout to set, if opp is NEXT or PREVIOUS
+ *
+ * @param viewId The view that is currently active
+ * @param opp The layout operation to perform
+ */
+ public static String getPreviousOrNextLayout(String viewId, LayoutOpp opp)
+ {
+ String prevNextLayout = null;
+ AbstractAndroidView view = (AbstractAndroidView) EclipseUtils.getActiveView(viewId);
+ if (view != null)
+ {
+ prevNextLayout = view.getPreviousOrNextLayout(opp);
+ }
+
+ return prevNextLayout;
+ }
+
+ /**
+ * Gets the layout to set, if opp is NEXT or PREVIOUS
+ *
+ * @param opp The layout operation to perform
+ */
+ @SuppressWarnings("incomplete-switch")
+ private String getPreviousOrNextLayout(LayoutOpp opp)
+ {
+ String prevNextLayout = null;
+ if (activeInstance != null)
+ {
+ String referenceLayout = activeInstance.getCurrentLayout();
+ AndroidViewData viewData = instanceDataMap.get(activeInstance);
+ if (viewData != null)
+ {
+ IAndroidComposite androidComposite = viewData.getComposite();
+ if ((androidComposite != null))
+ {
+ IAndroidSkin androidSkin = viewData.getSkin();
+
+ if (androidSkin != null)
+ {
+ switch (opp)
+ {
+ case NEXT:
+ prevNextLayout = androidSkin.getNextLayout(referenceLayout);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return prevNextLayout;
+ }
+
+ /**
+ * Updates the zoom action that needs to be checked in all emulator views.
+ * This method must be called every time the focus changes to another
+ * viewer.
+ *
+ * @param layoutName The layout name to set
+ */
+ public static void changeLayout(String layoutName)
+ {
+ for (String viewId : childrenIDs)
+ {
+ AbstractAndroidView view = (AbstractAndroidView) EclipseUtils.getActiveView(viewId);
+ if (view != null)
+ {
+ view.updateActiveViewer(layoutName);
+ }
+ }
+ }
+
+ /**
+ * Gets the help ID to be used for attaching
+ * context sensitive help.
+ *
+ * Classes that extends this class and want to set
+ * their on help should override this method
+ */
+ protected String getHelpId()
+ {
+ return IUIHelpConstants.EMULATOR_VIEW_HELP;
+ }
+
+ /**
+ * @see org.eclipse.ui.IWorkbenchPart#createPartControl(Composite)
+ */
+ @Override
+ public void createPartControl(Composite parent)
+ {
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, getHelpId());
+
+ this.tabFolder = new TabFolder(parent, SWT.BORDER | SWT.BACKGROUND);
+ IViewSite viewSite = getViewSite();
+
+ // Add listeners
+ tabFolder.addSelectionListener(new SelectionAdapter()
+ {
+ @Override
+ public void widgetSelected(SelectionEvent event)
+ {
+ setActiveInstanceId();
+ updateMenuAndToolbars();
+ handleTabSwitchEvent();
+ }
+ });
+
+ tabFolder.addFocusListener(new FocusAdapter()
+ {
+
+ @Override
+ public void focusGained(FocusEvent e)
+ {
+ handleTabSwitchEvent();
+ }
+
+ });
+
+ perspectiveListenerImpl = new PerspectiveListenerImpl();
+ viewSite.getWorkbenchWindow().addPerspectiveListener(perspectiveListenerImpl);
+
+ partListenerImpl = new PartListenerImpl();
+ viewSite.getPage().addPartListener(partListenerImpl);
+
+ if (workbenchListenerImpl == null)
+ {
+ workbenchListenerImpl = new WorkbenchListenerImpl();
+ viewSite.getWorkbenchWindow().getWorkbench()
+ .addWorkbenchListener(workbenchListenerImpl);
+ }
+
+ // Update UI
+ refreshView();
+
+ IActionBars actionBars = viewSite.getActionBars();
+ if (actionBars != null)
+ {
+ IMenuManager menuManager = actionBars.getMenuManager();
+ if (menuManager != null)
+ {
+ menuManager.addMenuListener(new IMenuListener()
+ {
+ public void menuAboutToShow(IMenuManager manager)
+ {
+ // Calls the manager update method to guarantee that the command have its handler
+ // initialized. Otherwise, the next command will not work properly
+ if (manager != null)
+ {
+ manager.update(true);
+ }
+ updateMenuAndToolbars();
+ }
+ });
+ }
+ }
+
+ //register the popup menu
+ viewSite.registerContextMenu(POPUP_MENU_ID, menuManager, null);
+
+ //create listener
+ if (Platform.getOS().contains(Platform.OS_MACOSX))
+ {
+ mouseClickListener = new MouseListener()
+ {
+
+ public void mouseDoubleClick(MouseEvent e)
+ {
+ //do nothing
+ }
+
+ public void mouseDown(MouseEvent e)
+ {
+ if ((e.button == 1) && (e.stateMask == SWT.CONTROL))
+ {
+ menuManager.getMenu().setVisible(true);
+ }
+ }
+
+ public void mouseUp(MouseEvent e)
+ {
+ //do nothing
+ }
+
+ };
+ }
+ else
+ {
+ mouseClickListener = new MouseListener()
+ {
+
+ public void mouseDoubleClick(MouseEvent e)
+ {
+ //do nothing
+ }
+
+ public void mouseDown(MouseEvent e)
+ {
+ if (e.button == 3)
+ {
+ menuManager.getMenu().setVisible(true);
+ }
+ }
+
+ public void mouseUp(MouseEvent e)
+ {
+ //do nothing
+ }
+
+ };
+
+ }
+
+ }
+
+ /**
+ * Constructor default
+ */
+ public AbstractAndroidView()
+ {
+ childrenIDs.add(getViewId());
+ menuManager = new MenuManager("", POPUP_MENU_ID);
+ addTabSwitchListener(listener);
+ }
+
+ /**
+ * @see org.eclipse.ui.IWorkbenchPart#setFocus()
+ */
+ @Override
+ public void setFocus()
+ {
+ if (tabFolder.getItemCount() > 0)
+ {
+ TabItem activeTabItem = getActiveTabItem();
+
+ if ((activeTabItem != null) && (activeTabItem.getControl() != null))
+ {
+ info("Setting focus to Android Emulator " + activeTabItem.getData());
+ activeTabItem.getControl().setFocus();
+ }
+ else
+ {
+ info("Setting focus to Android Emulator View");
+ tabFolder.setFocus();
+ }
+ }
+ else
+ {
+ info("Setting focus to Android Emulator View");
+ tabFolder.setFocus();
+ }
+
+ updateMenuAndToolbars();
+ }
+
+ /**
+ * @see org.eclipse.ui.IWorkbenchPart#dispose()
+ */
+ @Override
+ public void dispose()
+ {
+ removeTabSwitchListener(listener);
+ debug("Disposing View: " + getClass());
+ getViewSite().getWorkbenchWindow().removePerspectiveListener(perspectiveListenerImpl);
+ getViewSite().getPage().removePartListener(partListenerImpl);
+ perspectiveListenerImpl = null;
+ partListenerImpl = null;
+ instanceDataMap.clear();
+ tabFolder.dispose();
+ childrenIDs.remove(getViewId());
+ super.dispose();
+ }
+
+ /**
+ * This method rebuilds the skin, adding a new tab in the Android Emulator View
+ * to show it.
+ *
+ * It should be used when the Android Emulator view is being created when the Android Emulator
+ * instance is not stopped.
+ */
+ public void refreshView()
+ {
+ Job refreshViews = new Job("Refresh Emulator View")
+ {
+ @Override
+ protected IStatus run(IProgressMonitor monitor)
+ {
+
+ info("Updating Android Emulator viewers");
+
+ final DeviceFrameworkManager framework = DeviceFrameworkManager.getInstance();
+
+ Collection<IAndroidEmulatorInstance> startedInstances =
+ framework.getAllStartedInstances();
+
+ for (final IAndroidEmulatorInstance instance : startedInstances)
+ {
+ if (instance
+ .getProperties()
+ .getProperty(IDevicePropertiesOSConstants.useVnc,
+ NativeUIUtils.getDefaultUseVnc()).equals("true"))
+ {
+ if (!instance.isConnected())
+ {
+ IStatus returnStatus = null;
+ returnStatus = connectVNC(instance, monitor);
+ if (returnStatus.isOK())
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ createViewer(instance);
+ }
+ });
+ }
+ }
+ }
+ }
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+
+ Collection<IAndroidEmulatorInstance> connectedInstances =
+ framework.getAllConnectedInstances();
+
+ Collection<IAndroidEmulatorInstance> instancesWithViewerCollection =
+ getInstancesWithViewer();
+
+ for (IAndroidEmulatorInstance instance : connectedInstances)
+ {
+ if (!instancesWithViewerCollection.contains(instance))
+ {
+ createViewer(instance);
+ }
+ else
+ {
+ // update the collection for removing the stopped instances
+ instancesWithViewerCollection.remove(instance);
+ }
+ }
+
+ // Remove not started instances from viewer
+ for (IAndroidEmulatorInstance instance : instancesWithViewerCollection)
+ {
+ disposeViewer(instance);
+ info("Disposed viewer of " + instance);
+ }
+
+ // Update the active instance variable after any creation/disposal is
+ // made. Update the active viewer only if the active viewer is new
+ activeInstance = getActiveInstanceFromCurrentView();
+ if (activeInstance != null)
+ {
+ setActiveInstanceId();
+ handleTabSwitchEvent();
+ }
+
+ updateMenuAndToolbars();
+ }
+
+ });
+
+ return Status.OK_STATUS;
+ }
+ };
+ refreshViews.setRule(new RefreshRule());
+ refreshViews.schedule();
+ }
+
+ class RefreshRule implements ISchedulingRule
+ {
+ public boolean contains(ISchedulingRule rule)
+ {
+ return this == rule;
+ }
+
+ public boolean isConflicting(ISchedulingRule rule)
+ {
+ return rule instanceof RefreshRule;
+ }
+ }
+
+ /**
+ * Updates the zoom action that needs to be checked.
+ * This method must be called every time the focus changes to another
+ * viewer.
+ */
+ public void updateActiveViewer()
+ {
+ updateActiveViewer(null);
+ }
+
+ /**
+ * Updates the zoom action that needs to be checked, after performing a layout operation
+ *
+ * @param layoutName The name of the layout to set if opp is SETLAYOUT
+ */
+ public void updateActiveViewer(String layoutName)
+ {
+ info("Updating Android Emulator view");
+
+ if (activeInstance != null)
+ {
+ AndroidViewData viewData = instanceDataMap.get(activeInstance);
+ if (viewData != null)
+ {
+ IAndroidComposite androidComposite = viewData.getComposite();
+ if ((androidComposite != null))
+ {
+ if ((activeInstance.getProperties().getProperty(
+ IDevicePropertiesOSConstants.useVnc, NativeUIUtils.getDefaultUseVnc()))
+ .equals("true"))
+ {
+ IAndroidSkin androidSkin = viewData.getSkin();
+
+ if (androidSkin != null)
+ {
+ if (layoutName != null)
+ {
+ activeInstance.setCurrentLayout(layoutName);
+ }
+
+ boolean isNeeded =
+ androidSkin.isSwapWidthHeightNeededAtLayout(activeInstance
+ .getCurrentLayout());
+ IRemoteDisplay.Rotation rotation =
+ (isNeeded
+ ? IRemoteDisplay.Rotation.ROTATION_90DEG_COUNTERCLOCKWISE
+ : IRemoteDisplay.Rotation.ROTATION_0DEG);
+ viewData.getMainDisplay().setRotation(rotation);
+ androidComposite.applyLayout(activeInstance.getCurrentLayout());
+ }
+ }
+ androidComposite.applyZoomFactor();
+ }
+
+ }
+ }
+
+ updateMenuAndToolbars();
+
+ info("Updated Android Emulator view");
+ }
+
+ public void changeToNextLayout()
+ {
+ AndroidViewData viewData = instanceDataMap.get(activeInstance);
+ IAndroidComposite androidComposite = viewData.getComposite();
+ if (androidComposite instanceof NativeWindowComposite)
+ {
+ ((NativeWindowComposite) androidComposite).changeToNextLayout();
+ }
+ }
+
+ /**
+ * Retrieves the instance being currently displayed at this view.
+ *
+ * @return The active instance, or null if there is no active instance
+ */
+ private IAndroidEmulatorInstance getActiveInstanceFromCurrentView()
+ {
+ TabItem activeInstanceItem = getActiveTabItem();
+ IAndroidEmulatorInstance instance = null;
+ if (activeInstanceItem != null)
+ {
+ instance = (IAndroidEmulatorInstance) (activeInstanceItem.getData());
+ }
+ else
+ {
+ debug("No active instance being shown at emulator view");
+ }
+
+ return instance;
+ }
+
+ /**
+ * Executes the procedure to connect to the VNC
+ *
+ * @param androidDevice
+ * The device being connected
+ */
+ public IStatus connectVNC(final IAndroidEmulatorInstance instance, IProgressMonitor monitor)
+ {
+ IStatus statusToReturn = Status.OK_STATUS;
+
+ try
+ {
+ IAndroidLogicInstance logicInstance = (IAndroidLogicInstance) instance;
+ AbstractStartAndroidEmulatorLogic startLogic = logicInstance.getStartLogic();
+
+ startLogic.execute(logicInstance, LogicMode.TRANSFER_AND_CONNECT_VNC,
+ logicInstance.getTimeout(), monitor);
+ }
+ catch (StartCancelledException e1)
+ {
+ info("The user canceled the transfer/connect to VNC phase.");
+ statusToReturn = Status.CANCEL_STATUS;
+ }
+ catch (Exception e1)
+ {
+ error("Could not establish VNC Connection to " + instance);
+ statusToReturn =
+ new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID, NLS.bind(
+ EmulatorNLS.ERR_CannotConnectToVNC, instance.getName()));
+ }
+ return statusToReturn;
+ }
+
+ /**
+ * Shows the Android Emulator view, if not being shown
+ */
+ public static void showView()
+ {
+ info("Open and move focus to the emulator view");
+
+ boolean emulatorViewOpened =
+ !EclipseUtils.getAllOpenedViewsWithId(AndroidView.ANDROID_VIEW_ID).isEmpty();
+
+ try
+ {
+ // if emulator view is opened previously or if no emulator view is opened,
+ // show / refresh the emulator view.
+ if (emulatorViewOpened)
+ {
+ EclipseUtils.showView(AndroidView.ANDROID_VIEW_ID);
+ }
+ else
+ {
+ // Make sure only one open view (due to the transition to online) will occur at the same time.
+ // e.g. if the "question open dialog" is already opened, it is not needed one
+
+ if (showViewLock.tryLock())
+ {
+ try
+ {
+
+ boolean openEmulatorView =
+ DialogWithToggleUtils
+ .showQuestion(
+ SHOW_EMULATOR_IN_THE_IDE_KEY_PREFERENCE,
+ EmulatorNLS.QUESTION_AbstractAndroidView_OpenViewForStartedEmulatorsTitle,
+ EmulatorNLS.QUESTION_AbstractAndroidView_OpenViewForStartedEmulatorsMessage);
+ if (openEmulatorView)
+ {
+ EclipseUtils.showView(AndroidView.ANDROID_VIEW_ID);
+ }
+ }
+ finally
+ {
+ showViewLock.unlock();
+ }
+ }
+ }
+ }
+ catch (PartInitException e)
+ {
+ error("The Android Emulator View could not be opened programatically");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_AbstractAndroidView_ViewNotAccessibleProgramatically);
+ }
+ }
+
+ /**
+ * Creates a viewer for the provided instance
+ *
+ * @param instance The instance that will have a viewer created at this view
+ */
+ private void createViewer(final IAndroidEmulatorInstance instance)
+ {
+ if (instance != null)
+ {
+ info("Creating tab for " + instance + " on " + getClass());
+
+ Set<IAndroidEmulatorInstance> currentInstancesWithTab =
+ getInstancesWithAtLeastOneViewer();
+
+ // Creates a tab item to hold the skin at the view
+ TabItem newTabItem = new TabItem(tabFolder, SWT.NONE);
+
+ // Set parameters at the tab item
+ newTabItem.setText(instance.getFullName());
+ newTabItem.setData(instance);
+ AndroidViewData emulatorData = new AndroidViewData();
+ instanceDataMap.put(instance, emulatorData);
+
+ try
+ {
+ createWidgets(newTabItem, instance, emulatorData);
+ tabFolder.setSelection(newTabItem);
+ setActiveInstanceId();
+
+ //add popup menu
+ if (newTabItem.getControl() != null)
+ {
+ menuManager.createContextMenu(newTabItem.getControl());
+ newTabItem.getControl().addMouseListener(mouseClickListener);
+ }
+
+ ProtocolMessage setEncodingMsg = new ProtocolMessage(2);
+ setEncodingMsg.setFieldValue("padding", 0);
+ setEncodingMsg.setFieldValue("number-of-encodings", 1);
+ setEncodingMsg.setFieldValue("encoding-type", "encoding-types", 0, 0);
+ PluginProtocolActionDelegate.sendMessageToServer(instance.getProtocolHandle(),
+ setEncodingMsg);
+
+ info("Created tab for " + instance);
+
+ if (instance
+ .getProperties()
+ .getProperty(IDevicePropertiesOSConstants.useVnc,
+ NativeUIUtils.getDefaultUseVnc()).toString().equals("true"))
+ {
+ startVncDisplays(instance);
+ info("Started displays for " + instance);
+
+ // overwrite original tml listeners
+ addListenersToMainDisplay(emulatorData);
+ }
+ else
+ {
+ IAndroidComposite parentComposite = emulatorData.getComposite();
+ ((NativeWindowComposite) parentComposite).addMouseListener(mouseClickListener);
+ }
+
+ IAndroidComposite androidComposite = emulatorData.getComposite();
+ if (androidComposite != null)
+ {
+ androidComposite.applyZoomFactor();
+ }
+
+ // If this is the first view to be opened, guarantee that the screen orientation is
+ // synchronized with the current layout (only when using VNC)
+ if (!currentInstancesWithTab.contains(instance)
+ && instance
+ .getProperties()
+ .getProperty(IDevicePropertiesOSConstants.useVnc,
+ NativeUIUtils.getDefaultUseVnc()).toString().equals("true"))
+ {
+ IAndroidSkin skin = getSkin(instance);
+ if (skin != null)
+ {
+ instance.changeOrientation(skin.getLayoutScreenCommand(instance
+ .getCurrentLayout()));
+ }
+ }
+
+ updateActiveViewer();
+
+ info("Created tab for Android Emulator " + instance);
+ }
+ catch (SkinException e)
+ {
+ error("The skin associated to this instance (" + instance.getName()
+ + ") is not installed or is corrupted.");
+ EclipseUtils.showErrorDialog(e);
+
+ try
+ {
+ instance.stop(true);
+ disposeViewer(instance);
+ }
+ catch (InstanceStopException e1)
+ {
+ error("Error while running service for stopping virtual machine");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_General_CannotRunStopService);
+ }
+ }
+ }
+ }
+
+ private void addListenersToMainDisplay(AndroidViewData emulatorData)
+ {
+ // TmL registers listeners during start, and unregisters all of them during
+ // stop. To adapt the listeners to Studio needs, we are including the following
+ // operations after the start display call from TmL. With this we are achieving:
+ //
+ // 1. TmL registers several listeners at its canvas (TmL start method)
+ // 2. Studio unregisters the TmL listeners (this method)
+ // 3. Studio registers new listeners to replace the TmL ones (this method)
+ // 4. TmL unregisters Studio listeners instead of its own (TmL stop method)
+
+ SWTRemoteDisplay remoteDisplay = emulatorData.getMainDisplay();
+ final Canvas canvas = remoteDisplay.getCanvas();
+ IAndroidComposite parentComposite = emulatorData.getComposite();
+
+ for (int eventType : SWT_EVENT_TYPES)
+ {
+ for (Listener listener : canvas.getListeners(eventType))
+ {
+ canvas.removeListener(eventType, listener);
+ }
+ }
+
+ KeyListener keyListener = parentComposite.getKeyListener();
+ final MouseListener mouseListener = parentComposite.getMouseListener();
+ MouseMoveListener mouseMoveListener = parentComposite.getMouseMoveListener();
+
+ canvas.addKeyListener(keyListener);
+ canvas.addMouseListener(mouseListener);
+ canvas.addMouseMoveListener(mouseMoveListener);
+
+ // Due to the differences in listener registration between TmL and Studio, it will
+ // remain a registered listener when the viewer is disposed. For this reason, the
+ // following dispose listener is being registered.
+ DisposeListener disposeListener = new DisposeListener()
+ {
+ public void widgetDisposed(DisposeEvent arg0)
+ {
+ canvas.removeMouseListener(mouseListener);
+ canvas.removeMouseListener(mouseClickListener);
+ }
+ };
+ emulatorData.setDisposeListener(disposeListener);
+ canvas.addDisposeListener(disposeListener);
+ canvas.addMouseListener(mouseClickListener);
+ }
+
+ /**
+ * Disposes the viewer of the provided instance
+ *
+ * @param instance The instance that will have a viewer disposed from this view
+ */
+ private void disposeViewer(final IAndroidEmulatorInstance instance)
+ {
+ info("Disposing tab of Android Emulator at " + instance);
+
+ TabItem item = getTabItem(instance);
+ if (item != null)
+ {
+
+ stopVncDisplays(instance);
+
+ //if there are no other viewers, we can stop protocol and vnc server
+ if ((childrenIDs.size() == 1)
+ && (instance
+ .getProperties()
+ .getProperty(IDevicePropertiesOSConstants.useVnc,
+ NativeUIUtils.getDefaultUseVnc()).toString().equals("true")))
+ {
+ info("There is only one view opened, stop VNC protocol and VNC Server");
+ stopVncProtocol((IAndroidLogicInstance) instance);
+ stopVncServer(instance);
+ }
+
+ AndroidViewData data = instanceDataMap.get(instance);
+ if (data != null)
+ {
+ SWTRemoteDisplay mainDisplay = data.getMainDisplay();
+ if (mainDisplay != null)
+ {
+ Canvas canvas = mainDisplay.getCanvas();
+
+ if (canvas != null)
+ {
+ canvas.removeDisposeListener(data.getDisposeListener());
+ }
+ }
+ }
+
+ Control c = item.getControl();
+ if (c != null)
+ {
+ c.dispose();
+ }
+ item.setControl(null);
+
+ item.dispose();
+ instanceDataMap.remove(instance);
+ updateMenuAndToolbars();
+ info("Disposed tab of Android Emulator at " + instance);
+ }
+
+ }
+
+ /**
+ * Gets the list of instances with viewers associated.
+ * @return the collection of instances
+ */
+ private Collection<IAndroidEmulatorInstance> getInstancesWithViewer()
+ {
+ final Collection<IAndroidEmulatorInstance> instancesWithViewer =
+ new LinkedHashSet<IAndroidEmulatorInstance>();
+
+ if (!tabFolder.isDisposed())
+ {
+ final TabItem[] allItems = tabFolder.getItems();
+
+ for (TabItem item : allItems)
+ {
+ if (!item.isDisposed())
+ {
+ instancesWithViewer.add((IAndroidEmulatorInstance) item.getData());
+ }
+ }
+ }
+
+ return instancesWithViewer;
+ }
+
+ private static Set<IAndroidEmulatorInstance> getInstancesWithAtLeastOneViewer()
+ {
+ Set<IAndroidEmulatorInstance> instancesSet = new HashSet<IAndroidEmulatorInstance>();
+ for (String viewId : childrenIDs)
+ {
+ AbstractAndroidView view = (AbstractAndroidView) EclipseUtils.getActiveView(viewId);
+ if (view != null)
+ {
+ instancesSet.addAll(view.getInstancesWithViewer());
+ }
+ }
+
+ return instancesSet;
+ }
+
+ /**
+ * Gets the tab item related to the instance
+ * @param instance the emulator instance
+ * @return the tab item
+ */
+ private TabItem getTabItem(IAndroidEmulatorInstance instance)
+ {
+ TabItem result = null;
+ if (!tabFolder.isDisposed())
+ {
+ TabItem[] allItems = tabFolder.getItems();
+
+ for (TabItem item : allItems)
+ {
+ if (instance.equals(item.getData()))
+ {
+ result = item;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the tab item related to the instance
+ * @param instance the emulator instance
+ * @return the tab item
+ */
+ private TabItem getTabItem(IInstance instance)
+ {
+ TabItem result = null;
+ if (!tabFolder.isDisposed())
+ {
+ TabItem[] allItems = tabFolder.getItems();
+
+ for (TabItem item : allItems)
+ {
+ if (instance.getName()
+ .equals(((IAndroidEmulatorInstance) item.getData()).getName()))
+ {
+ result = item;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Retrieves the active tab at view
+ *
+ * @return The active tab, or null of there is no tab at tab folder
+ */
+ private TabItem getActiveTabItem()
+ {
+ int activeInstanceIndex = this.tabFolder.getSelectionIndex();
+
+ TabItem activeTabItem = null;
+
+ if (activeInstanceIndex >= 0)
+ {
+ activeTabItem = this.tabFolder.getItem(activeInstanceIndex);
+ }
+
+ return activeTabItem;
+ }
+
+ /**
+ * Updates the zoom action that needs to be checked.
+ * This method must be called every time the focus changes to another
+ * viewer
+ */
+ private void updateMenuAndToolbars()
+ {
+ IViewSite viewSite = getViewSite();
+
+ if (viewSite != null)
+ {
+ IActionBars actionBars = viewSite.getActionBars();
+
+ if (actionBars != null)
+ {
+ IMenuManager menuManager = actionBars.getMenuManager();
+ updateMenuManager(menuManager, viewSite);
+
+ IToolBarManager toolbarManager = actionBars.getToolBarManager();
+ if (toolbarManager != null)
+ {
+ IContributionItem[] items = toolbarManager.getItems();
+ for (IContributionItem item : items)
+ {
+ item.update();
+ }
+ }
+ }
+
+ refreshMenuElements();
+ }
+ }
+
+ /**
+ * Recursive method to update items at menus. The recursion helps to update submenus
+ *
+ * @param manager The manager that holds a menu items
+ * @param viewSite The current view site.
+ */
+ private void updateMenuManager(IMenuManager manager, IViewSite viewSite)
+ {
+ // Update the items in menu manager
+ if (manager != null)
+ {
+ IContributionItem[] items = manager.getItems();
+ for (IContributionItem item : items)
+ {
+ if (item instanceof IMenuManager)
+ {
+ updateMenuManager((IMenuManager) item, viewSite);
+ }
+ else
+ {
+ item.update();
+ }
+ }
+ }
+ }
+
+ /**
+ * Stops all emulator instances with the Progress Monitor opened.
+ */
+ private void stopEmulatorInstances()
+ {
+ // defines the runnable object for stopping emulator instances.
+ final IRunnableWithProgress stopRunnable = new IRunnableWithProgress()
+ {
+ public void run(IProgressMonitor monitor)
+ {
+ Collection<IAndroidEmulatorInstance> startedInstances =
+ DeviceFrameworkManager.getInstance().getAllStartedInstances();
+ boolean errorsHappened = false;
+
+ for (IAndroidEmulatorInstance instance : startedInstances)
+ {
+ try
+ {
+ instance.stop(true);
+ }
+ catch (InstanceStopException e)
+ {
+ errorsHappened = true;
+ }
+ }
+
+ // if closing on shutdown, use a progress bar and stall UI
+ if (closingOnShutdown)
+ {
+ // start a progress monitor
+ monitor.beginTask("", IProgressMonitor.UNKNOWN);
+
+ // make sure the stop instance job finished
+ Job[] jobs = Job.getJobManager().find(null); // get all jobs
+ for (Job job : jobs)
+ {
+ if (job.getName()
+ .equals(EmulatorNLS.UI_AbstractAndroidView_StopInstanceJob))
+ {
+ // when job result is not null, it has finished
+ while (job.getResult() == null)
+ {
+ try
+ {
+ // sleep a little so the waiting is not too busy
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException e)
+ {
+ // do nothing
+ }
+ }
+ }
+ }
+ }
+
+ if (errorsHappened)
+ {
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_AncroidView_CannotRunMultipleStopServices);
+ }
+
+ }
+ };
+
+ // executes the runnable defined above.
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ Shell currentShell = getViewSite().getShell();
+ ProgressMonitorDialog dialog = new ProgressMonitorDialog(currentShell);
+ try
+ {
+ dialog.run(true, false, stopRunnable);
+ }
+ catch (Exception e)
+ {
+ // Should not have exceptions.
+ // The runnable is not interrupted and it handles exceptions internally
+ // Log runtime exceptions
+ error("Runtime exception was thrown: " + e.getClass().getSimpleName());
+ }
+ }
+ });
+ }
+
+ /**
+ * Sets the identifier of the instance being currently displayed at view
+ */
+ private void setActiveInstanceId(String activeHost)
+ {
+ for (String viewId : childrenIDs)
+ {
+ Collection<IViewPart> viewsToUpdateMenu = EclipseUtils.getAllOpenedViewsWithId(viewId);
+ for (IViewPart view : viewsToUpdateMenu)
+ {
+ AbstractAndroidView emulatorView = (AbstractAndroidView) view;
+ emulatorView.setSelection(activeHost);
+ }
+
+ }
+ }
+
+ /**
+ * Sets the identifier of the instance being currently displayed at view
+ */
+ private void setActiveInstanceId()
+ {
+ TabItem activeInstanceItem = getActiveTabItem();
+ if ((activeInstanceItem != null) && (activeInstanceItem.getData() != null))
+ {
+
+ activeInstance = (IAndroidEmulatorInstance) activeInstanceItem.getData();
+
+ String activeId =
+ ((IAndroidEmulatorInstance) activeInstanceItem.getData())
+ .getInstanceIdentifier();
+
+ setActiveInstanceId(activeId);
+ }
+ else
+ {
+ debug("No active instance being shown at emulator view");
+ }
+
+ }
+
+ /**
+ * Starts the main display associating it to the protocol.
+ * @param handle the protocol handle
+ * @param mainDisplay the main display object
+ */
+ private void startDisplay(ProtocolHandle handle, SWTRemoteDisplay mainDisplay)
+ {
+ // Stop any running screens
+ if ((mainDisplay.isActive()) && (!mainDisplay.isDisposed()))
+ {
+ mainDisplay.stop();
+ }
+
+ try
+ {
+ info("Starting main display refresh");
+ mainDisplay.start(handle);
+ }
+ catch (Exception e)
+ {
+ error("Viewers could not be started.");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_AndroidView_ErrorStartingScreens);
+
+ GC gc = new GC(mainDisplay.getCanvas());
+ gc.fillRectangle(0, 0, mainDisplay.getScreenWidth(), mainDisplay.getScreenHeight());
+ gc.dispose();
+ }
+ }
+
+ /**
+ * Starts viewer (main display and CLI display) of the emulator instance.
+ * @param instance the emulator instance
+ */
+ protected void startVncDisplays(final IAndroidEmulatorInstance instance)
+ {
+ AndroidViewData viewData = instanceDataMap.get(instance);
+ if (viewData != null)
+ {
+ if (viewData.getMainDisplay() != null)
+ {
+ startDisplay(instance.getProtocolHandle(), viewData.getMainDisplay());
+ }
+ if ((viewData.getCliDisplay() != null) && instance.getHasCli())
+ {
+ viewData.getCliDisplay().start();
+ }
+ }
+ }
+
+ /**
+ * Stops viewer (main display and CLI display) of the emulator instance.
+ */
+ private void stopVncDisplays(final IAndroidEmulatorInstance instance)
+ {
+ info("Stop the VNC Display " + getViewId() + " for " + instance);
+ AndroidViewData viewData = instanceDataMap.get(instance);
+
+ if ((viewData != null))
+ {
+ SWTRemoteDisplay mainDisplay = viewData.getMainDisplay();
+ if ((mainDisplay != null) && mainDisplay.isActive() && !mainDisplay.isDisposed())
+ {
+ mainDisplay.stop();
+ if ((mainDisplay.getBackground() != null)
+ && !mainDisplay.getBackground().isDisposed())
+ {
+ mainDisplay.getBackground().dispose();
+ }
+ }
+
+ RemoteCLIDisplay cliDisplay = viewData.getCliDisplay();
+ if ((cliDisplay != null) && cliDisplay.isDisplayActive() && !cliDisplay.isDisposed())
+ {
+ cliDisplay.stop();
+ if ((cliDisplay.getBackground() != null)
+ && !cliDisplay.getBackground().isDisposed())
+ {
+ cliDisplay.getBackground().dispose();
+ }
+ }
+ }
+ }
+
+ /**
+ * @param instance
+ */
+ private void stopVncProtocol(IAndroidLogicInstance instance)
+ {
+ AndroidEmulatorStopper.stopInstance(instance, true, false, new NullProgressMonitor());
+
+ }
+
+ /**
+ * Stops the execution of the vnc server if it is running on the given instance.
+ * This acts as if the Control+C was pressed in the shell where the vnc server is executing...
+ * @param instance
+ */
+ private void stopVncServer(IAndroidEmulatorInstance instance)
+ {
+ StartVncServerLogic.cancelCurrentVncServerJobs(instance);
+ }
+
+ /**
+ * Class to implement the IPerspectiveListener that you be used as ParListener2 of
+ * current page when the Emulator View is opened. It is required to code the
+ * work-around for Sticky Views on perspectiveChanged method.
+ */
+ private class PerspectiveListenerImpl implements IPerspectiveListener
+ {
+
+ public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspective)
+ {
+ // Nothing to do.
+ }
+
+ public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective,
+ String changeId)
+ {
+ if (changeId.equals(IWorkbenchPage.CHANGE_VIEW_HIDE))
+ {
+
+ // if the emulator view was hidden
+ if (page.findView(getViewId()) == null)
+ {
+
+ // This is a "sticky view" so when it is hidden or shown for one
+ // perspective, its state is remembered for the other ones.
+ // However, the view reference count is just updated when
+ // the current active perspective is changed. The code below
+ // forces the perspective changing in order to dispose the view
+ // immediately after it is hidden.
+ for (IPerspectiveDescriptor pd : page.getOpenPerspectives())
+ {
+ if (!pd.equals(perspective))
+ {
+ page.setPerspective(pd);
+ }
+ }
+ page.setPerspective(perspective);
+ }
+ }
+ }
+ }
+
+ /**
+ * Class to implement IPartListener2 that you be used as ParListener2 of current page
+ * when the Emulator View is opened. It is necessary to determine when the view is
+ * closed.
+ */
+ private class PartListenerImpl implements IPartListener2
+ {
+
+ public void partActivated(IWorkbenchPartReference partRef)
+ {
+ // Nothing to do.
+ }
+
+ public void partBroughtToTop(IWorkbenchPartReference partRef)
+ {
+ // Nothing to do.
+ }
+
+ public void partClosed(final IWorkbenchPartReference partRef)
+ {
+
+ // if view that is being closed is not THIS view.
+ if (!partRef.getId().equals(getViewId()))
+ {
+ return;
+ }
+
+ // executed on async mode to avoid UI blocking
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ boolean openedViewsExist = false;
+
+ for (String viewId : childrenIDs)
+ {
+ if (!getViewId().equals(viewId)
+ && (partRef.getPage().findView(viewId) != null))
+ {
+ openedViewsExist = true;
+ break;
+ }
+ }
+
+ // stops all viewers and clear the tabs list
+ Collection<IAndroidEmulatorInstance> instances = getInstancesWithViewer();
+ for (IAndroidEmulatorInstance instance : instances)
+ {
+ disposeViewer(instance);
+ }
+
+ // if the tool is not being closed and there is no other emulator
+ // view opened.
+ if (!closingOnShutdown && !openedViewsExist)
+ {
+
+ Collection<IAndroidEmulatorInstance> startedInstances =
+ DeviceFrameworkManager.getInstance().getAllStartedInstances();
+
+ boolean oneInstanceStarted = (startedInstances.size() > 0);
+
+ if (oneInstanceStarted
+ && (DialogWithToggleUtils
+ .showQuestion(
+ STOP_BY_CLOSING_VIEW_KEY_PREFERENCE,
+ EmulatorNLS.QUESTION_AndroidView_StopAllInstancesOnDisposeTitle,
+ EmulatorNLS.QUESTION_AndroidView_StopAllInstancesOnDisposeMessage)))
+ {
+
+ stopEmulatorInstances();
+ }
+ }
+
+ }
+
+ });
+ }
+
+ public void partDeactivated(IWorkbenchPartReference partRef)
+ {
+ // Nothing to do.
+ }
+
+ public void partHidden(IWorkbenchPartReference partRef)
+ {
+ // Nothing to do.
+ }
+
+ public void partInputChanged(IWorkbenchPartReference partRef)
+ {
+ // Nothing to do.
+ }
+
+ public void partOpened(IWorkbenchPartReference partRef)
+ {
+ // Nothing to do.
+ }
+
+ public void partVisible(IWorkbenchPartReference partRef)
+ {
+ if (partRef.getId().equals(getViewId()))
+ {
+ refreshView();
+ }
+ }
+ }
+
+ /**
+ * Class to implement the IWorkbenchListener that you be used as WorkbenchListener of
+ * workbench when the Emulator View is opened. It is used to know if the view
+ * is being closed due to workbench shutdown.
+ */
+ private class WorkbenchListenerImpl implements IWorkbenchListener
+ {
+
+ public void postShutdown(IWorkbench workbench)
+ {
+ // Nothing to do.
+ }
+
+ public boolean preShutdown(IWorkbench workbench, boolean forced)
+ {
+ closingOnShutdown = true;
+
+ Collection<IAndroidEmulatorInstance> startedInstances =
+ DeviceFrameworkManager.getInstance().getAllStartedInstances();
+
+ if (startedInstances.size() > 0)
+ {
+
+ boolean stopEmulatorInstances = false;
+ if (PluginUtils.getOS() != PluginUtils.OS_LINUX)
+ {
+ stopEmulatorInstances =
+ DialogWithToggleUtils.showQuestion(
+ STOP_ALL_EMULATORS_IN_SHUTDOWN_KEY_PREFERENCE,
+ EmulatorNLS.QUESTION_RunningInstancesOnClose_Title,
+ EmulatorNLS.QUESTION_RunningInstancesOnClose_Text);
+ }
+ else
+ {
+ DialogWithToggleUtils.showWarning(
+ STOP_ALL_EMULATORS_IN_SHUTDOWN_KEY_PREFERENCE,
+ EmulatorNLS.WARN_RunningInstancesOnClose_Linux_Title,
+ EmulatorNLS.WARN_RunningInstancesOnClose_Linux_Text);
+ //stopEmulatorInstances = true;
+ }
+
+ if (stopEmulatorInstances)
+ {
+ stopEmulatorInstances();
+ }
+
+ }
+
+ return true;
+ }
+ }
+
+ /**
+ * Selects the tab that has this data host and set the activeHost
+ * @param host IP address
+ */
+ private void setSelection(final String host)
+ {
+
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ TabItem selectedTab = null;
+
+ TabItem[] tabArray = tabFolder.getItems();
+
+ for (TabItem tabItem : tabArray)
+ {
+ String tabItemHost =
+ ((IAndroidEmulatorInstance) tabItem.getData()).getInstanceIdentifier();
+ if ((host != null) && (host.equals(tabItemHost)))
+ {
+ selectedTab = tabItem;
+ break;
+ }
+ }
+
+ if (selectedTab != null)
+ {
+ tabFolder.setSelection(selectedTab);
+ updateMenuAndToolbars();
+ }
+
+ }
+ });
+
+ }
+
+ public static void updateInstanceName(final IInstance instance)
+ {
+ PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
+ {
+ public void run()
+ {
+ if (!childrenIDs.isEmpty())
+ {
+ AbstractAndroidView view =
+ (AbstractAndroidView) EclipseUtils.getActiveView(childrenIDs.get(0));
+ if (view != null)
+ {
+ if ((instance != null))
+ {
+ TabItem tabItem = view.getTabItem(instance);
+ if (tabItem != null)
+ {
+ tabItem.setText(((IAndroidEmulatorInstance) tabItem.getData())
+ .getFullName());
+ }
+ }
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Sets the skin zoom factor
+ *
+ * @param instance the emulator instance
+ * @param zoom the zoom factor
+ */
+ public final void setZoomFactor(IAndroidEmulatorInstance instance, double zoom)
+ {
+ try
+ {
+ AndroidViewData viewData = instanceDataMap.get(instance);
+ if (viewData != null)
+ {
+ IAndroidComposite composite = viewData.getComposite();
+ if (composite != null)
+ {
+ composite.setZoomFactor(zoom);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ error("Detached zoom could not be set.");
+ }
+ }
+
+ /**
+ * Gets the skin zoom factor
+ *
+ * @param instance the emulator instance
+ * @return the zoom factor
+ */
+ public final double getZoomFactor(IAndroidEmulatorInstance instance)
+ {
+ double zoomFactor = 0.0;
+ AndroidViewData viewData = instanceDataMap.get(instance);
+ if (viewData != null)
+ {
+ IAndroidComposite composite = viewData.getComposite();
+ if (composite != null)
+ {
+ zoomFactor = composite.getZoomFactor();
+ }
+ }
+ return zoomFactor;
+ }
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AndroidView.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AndroidView.java
new file mode 100644
index 0000000..2a05611
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AndroidView.java
@@ -0,0 +1,265 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.view;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+import org.eclipse.sequoyah.vnc.vncviewer.config.IPropertiesFileHandler;
+import org.eclipse.sequoyah.vnc.vncviewer.config.VNCConfiguration;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.ISWTPainter;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.img.SWTRemoteDisplayImg;
+import org.eclipse.sequoyah.vnc.vncviewer.network.VNCProtocolData;
+import org.eclipse.sequoyah.vnc.vncviewer.registry.VNCProtocolRegistry;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.commands.ICommandService;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.core.skin.ISkinKeyXmlTags;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.ui.controls.IAndroidComposite;
+import com.motorola.studio.android.emulator.ui.controls.maindisplay.MainDisplayComposite;
+import com.motorola.studio.android.emulator.ui.controls.nativewindow.NativeWindowComposite;
+import com.motorola.studio.android.emulator.ui.controls.skin.SkinComposite;
+import com.motorola.studio.android.emulator.ui.handlers.IHandlerConstants;
+import com.motorola.studio.android.nativeos.IDevicePropertiesOSConstants;
+import com.motorola.studio.android.nativeos.NativeUIUtils;
+
+/**
+ * DESCRIPTION: This class represents the Android Emulator view
+ *
+ * RESPONSIBILITY: - Show the skin to the end user
+ *
+ * COLABORATORS: None.
+ *
+ * USAGE: All the public interface is extended from
+ * <code>AbstractAndroidView</code>.
+ */
+public class AndroidView extends AbstractAndroidView
+{
+ /**
+ * The Android Emulator view ID
+ */
+ public static final String ANDROID_VIEW_ID = EmulatorPlugin.PLUGIN_ID + ".androidView";
+
+ /**
+ * @see com.motorola.studio.android.emulator.ui.view.AbstractAndroidView#getViewId()
+ */
+ @Override
+ protected String getViewId()
+ {
+ return ANDROID_VIEW_ID;
+ }
+
+ /**
+ * Creates the skin composite, main display and CLI display related to
+ * emulator.
+ *
+ * @param tab
+ * the tab item that will hold the graphical elements that
+ * represents the emulator
+ * @param instance
+ * the emulator instance
+ * @param emulatorData
+ * the object to be defined with the elements created.
+ */
+ @Override
+ protected void createWidgets(TabItem tab, final IAndroidEmulatorInstance instance,
+ final AndroidViewData tabData) throws SkinException
+ {
+ tabData.loadSkin(instance);
+ IAndroidSkin skin = tabData.getSkin();
+
+ ProtocolHandle handle = instance.getProtocolHandle();
+ VNCProtocolData data = VNCProtocolRegistry.getInstance().get(handle);
+
+ final SWTRemoteDisplay mainDisplay;
+ Composite parentComposite;
+ if (instance.getProperties().getProperty(IDevicePropertiesOSConstants.useVnc,
+ NativeUIUtils.getDefaultUseVnc()).toString().equals("true"))
+ {
+ if (data != null)
+ {
+ if (instance.getProperties().getProperty("Command_Line").contains("-no-skin"))
+ {
+ int baseWidth =
+ skin.getSkinBean(instance.getCurrentLayout()).getSkinPropertyValue(
+ ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH);
+ int baseHeight =
+ skin.getSkinBean(instance.getCurrentLayout()).getSkinPropertyValue(
+ ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT);
+
+ ScrolledComposite scrolledComposite =
+ new ScrolledComposite(tab.getParent(), SWT.H_SCROLL | SWT.V_SCROLL
+ | SWT.BACKGROUND);
+
+ final MainDisplayComposite composite =
+ new MainDisplayComposite(scrolledComposite, SWT.BACKGROUND, baseWidth,
+ baseHeight, instance);
+ composite.setLayout(new FillLayout());
+
+ mainDisplay =
+ createMainDisplay(composite, skin, instance, (ISWTPainter) data
+ .getVncPainter());
+ composite.setSize(baseWidth, baseHeight);
+
+ scrolledComposite.setContent(composite);
+ tab.setControl(scrolledComposite);
+
+ scrolledComposite.addDisposeListener(new DisposeListener()
+ {
+ public void widgetDisposed(DisposeEvent e)
+ {
+ composite.dispose();
+ }
+ });
+
+ tabData.setCliDisplay(null);
+ tabData.setComposite(composite);
+ tabData.setMainDisplay(mainDisplay);
+ }
+ else
+ {
+ parentComposite = createSkinComposite(tab.getParent(), skin, instance);
+ mainDisplay =
+ createMainDisplay(parentComposite, skin, instance, (ISWTPainter) data
+ .getVncPainter());
+
+ tabData.setComposite((IAndroidComposite) parentComposite);
+ tabData.setMainDisplay(mainDisplay);
+ tab.setControl(parentComposite);
+ }
+ }
+ else
+ {
+ error("The protocol object set in the device instance is not supported. Stopping the emulator instance...");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.ERR_AndroidView_ProtocolImplementerNotSupported);
+
+ try
+ {
+ instance.stop(true);
+ }
+ catch (InstanceStopException e)
+ {
+ error("Error while running service for stopping virtual machine");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_General_CannotRunStopService);
+ }
+ }
+
+ }
+ else
+ {
+ parentComposite = createNativeWindowComposite(tab.getParent(), skin, instance);
+ tabData.setComposite((IAndroidComposite) parentComposite);
+ tab.setControl(parentComposite);
+ }
+ }
+
+ /**
+ * Creates the skin composite used by this instance
+ *
+ * @param parent
+ * The parent composite where to place the widgets
+ * @param skin
+ * The object containing skin information used to draw the
+ * widgets
+ */
+ private SkinComposite createSkinComposite(Composite parent, IAndroidSkin skin,
+ IAndroidEmulatorInstance instance)
+ {
+
+ SkinComposite skinComposite = new SkinComposite(parent, skin, instance);
+ skinComposite.setBackground(new Color(skinComposite.getDisplay(), 255, 255, 255));
+
+ return skinComposite;
+ }
+
+ private NativeWindowComposite createNativeWindowComposite(Composite parent, IAndroidSkin skin,
+ IAndroidEmulatorInstance instance)
+ {
+ NativeWindowComposite nativeWindowComposite =
+ new NativeWindowComposite(parent, skin, instance);
+ // nativeWindowComposite.setBackground(new Color(nativeWindowComposite.getDisplay(), 255, 255,
+ // 255));
+
+ return nativeWindowComposite;
+ }
+
+ /**
+ * Creates the main display object used by this instance.
+ *
+ * @param parent
+ * The parent composite where to place the widgets
+ * @param skin
+ * The object containing skin information used to draw the
+ * widgets
+ * @param instance
+ * The emulator instance
+ * @param painter
+ * the painter used to draw the images
+ */
+ private SWTRemoteDisplay createMainDisplay(Composite parent, IAndroidSkin skin,
+ IAndroidEmulatorInstance instance, ISWTPainter painter)
+
+ {
+ // gets and reads the configuration files of the VNC session
+ IPropertiesFileHandler handler = IAndroidSkin.DEFAULT_PROPS_HANDLER;
+ String configFile = IAndroidSkin.DEFAULT_VNC_CONFIG_FILE;
+
+ VNCConfiguration config = new VNCConfiguration(configFile, handler);
+ final SWTRemoteDisplay mainDisplay;
+ mainDisplay =
+ new SWTRemoteDisplayImg(parent, config.getConfigurationProperties(), handler,
+ painter);
+
+ mainDisplay.setBackground(new Color(mainDisplay.getDisplay(), 0, 0, 0));
+ mainDisplay.setLayout(new FillLayout());
+
+ return mainDisplay;
+ }
+
+ /**
+ * Forces the refreshing of the menu elements.
+ */
+ @Override
+ protected void refreshMenuElements()
+ {
+ IViewSite viewSite = getViewSite();
+
+ // Update the radio button selection in the zoom menu
+ ICommandService service = (ICommandService) viewSite.getService(ICommandService.class);
+ service.refreshElements(IHandlerConstants.CHANGE_EMULATOR_ORIENTATION_COMMAND, null);
+ service.refreshElements(IHandlerConstants.CHANGE_EMULATOR_ZOOM_COMMAND, null);
+
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AndroidViewData.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AndroidViewData.java
new file mode 100644
index 0000000..1116bb4
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AndroidViewData.java
@@ -0,0 +1,167 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.view;
+
+import static com.motorola.studio.android.common.log.StudioLogger.debug;
+
+import java.io.File;
+import java.util.Collection;
+
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay;
+import org.eclipse.swt.events.DisposeListener;
+
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.core.skin.SkinFramework;
+import com.motorola.studio.android.emulator.ui.controls.IAndroidComposite;
+import com.motorola.studio.android.emulator.ui.controls.RemoteCLIDisplay;
+
+/**
+ * AndroidViewData: this class is responsible for having the information about Main Display,
+ * CLI Display and the composite where these displays will be drawn.
+ */
+public class AndroidViewData
+{
+ /**
+ * Implementation of the
+ */
+ private IAndroidSkin skin;
+
+ /**
+ * Composite that shows the contents of the mobile main display
+ */
+ private SWTRemoteDisplay mainDisplay;
+
+ /**
+ * Composite that shows the contents of the mobile CLI display
+ */
+ private RemoteCLIDisplay cliDisplay;
+
+ /**
+ * Composite for view's components.
+ */
+ private IAndroidComposite composite;
+
+ /**
+ * Dispose listener of this instance
+ */
+ private DisposeListener disposeListener;
+
+ /**
+ * Loads the Android emulator skin of the given the AVD/instance
+ * @param instance whose skin will be loaded
+ * @throws SkinException if it is no possible to load the skin
+ */
+ public synchronized void loadSkin(IAndroidEmulatorInstance instance) throws SkinException
+ {
+ String skinId = instance.getSkinId();
+ SkinFramework skinFw = new SkinFramework();
+ File skinPath = instance.getSkinPath();
+ skin = skinFw.getSkinById(skinId, skinPath);
+ Collection<String> layoutNames = skin.getAvailableLayouts();
+ String currentLayout = instance.getCurrentLayout();
+ if ((currentLayout == null) && (!layoutNames.isEmpty()))
+ {
+ String firstLayout = layoutNames.iterator().next();
+ instance.setCurrentLayout(firstLayout);
+ debug("The skin has multiple layouts. Setting " + firstLayout + " as the current one.");
+ }
+
+ }
+
+ /**
+ * Retrieves the loaded IAndroidSkin.
+ * Returns null if no skin is loaded.
+ * @return
+ */
+ public synchronized IAndroidSkin getSkin()
+ {
+ return skin;
+ }
+
+ /**
+ * Gets the dispose listener
+ * @return dispose listener
+ */
+ DisposeListener getDisposeListener()
+ {
+ return disposeListener;
+ }
+
+ /**
+ * Sets the dispose listener
+ * @param disposeListener dispose listener
+ */
+ void setDisposeListener(DisposeListener disposeListener)
+ {
+ this.disposeListener = disposeListener;
+ }
+
+ /**
+ * Gets Main Display
+ * @return main display
+ */
+ public SWTRemoteDisplay getMainDisplay()
+ {
+ return mainDisplay;
+ }
+
+ /**
+ * Sets main Display
+ * @param mainDisplay main display
+ */
+ void setMainDisplay(SWTRemoteDisplay mainDisplay)
+ {
+ this.mainDisplay = mainDisplay;
+ }
+
+ /**
+ * Gets CLI Display
+ * @return CLI display
+ */
+ RemoteCLIDisplay getCliDisplay()
+ {
+ return cliDisplay;
+ }
+
+ /**
+ * Sets CLI Display
+ * @param cliDisplay CLI display
+ */
+ void setCliDisplay(RemoteCLIDisplay cliDisplay)
+ {
+ this.cliDisplay = cliDisplay;
+ }
+
+ /**
+ * Gets view composite
+ * @return composite
+ */
+ public IAndroidComposite getComposite()
+ {
+ return composite;
+ }
+
+ /**
+ * Sets view composite
+ * @param composite composite
+ */
+ void setComposite(IAndroidComposite composite)
+ {
+ this.composite = composite;
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/LayoutContributionItem.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/LayoutContributionItem.java
new file mode 100644
index 0000000..640ee37
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/LayoutContributionItem.java
@@ -0,0 +1,143 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.view;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jface.action.IContributionItem;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.CompoundContributionItem;
+import org.eclipse.ui.menus.CommandContributionItem;
+import org.eclipse.ui.menus.CommandContributionItemParameter;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.ui.handlers.IHandlerConstants;
+
+/**
+ * This class builds dynamically the Emulator Layouts submenu from the Android Emulator and
+ * Main Display views
+ **/
+public class LayoutContributionItem extends CompoundContributionItem
+{
+ // Layout dynamic constants
+ public static final String ANDROID_VIEW_LAYOUT_DYNAMIC_ID = "androidView.layout.dynamic";
+
+ public static final String MAIN_DISPLAY_VIEW_LAYOUT_DYNAMIC_ID =
+ "mainDisplayView.layout.dynamic";
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.actions.CompoundContributionItem#getContributionItems()
+ */
+ @Override
+ protected IContributionItem[] getContributionItems()
+ {
+ IContributionItem[] itemsArray;
+
+ AbstractAndroidView view = null;
+ String viewId = null;
+ if (getId().equals(ANDROID_VIEW_LAYOUT_DYNAMIC_ID))
+ {
+ viewId = AndroidView.ANDROID_VIEW_ID;
+ view = (AbstractAndroidView) EclipseUtils.getActiveView(viewId);
+ }
+ else if (getId().equals(MAIN_DISPLAY_VIEW_LAYOUT_DYNAMIC_ID))
+ {
+ viewId = MainDisplayView.EMULATOR_MAIN_DISPLAY_VIEW_ID;
+ view = (AbstractAndroidView) EclipseUtils.getActiveView(viewId);
+ }
+
+ if (view != null)
+ {
+ IAndroidEmulatorInstance instance = AbstractAndroidView.getActiveInstance();
+ IAndroidSkin skin = view.getSkin(instance);
+ if (skin != null)
+ {
+ Collection<String> layoutNames = skin.getAvailableLayouts();
+ itemsArray = new IContributionItem[layoutNames.size()];
+ populateContributionList(itemsArray, layoutNames, viewId);
+ }
+ else
+ {
+ itemsArray = new IContributionItem[1];
+ populateWithEmpty(itemsArray);
+ }
+ }
+ else
+ {
+ itemsArray = new IContributionItem[1];
+ populateWithEmpty(itemsArray);
+ }
+
+ return itemsArray;
+ }
+
+ /**
+ * Populates the array with a command item per layout name
+ *
+ * @param itemsArray The array to be populated
+ * @param layoutNames The items to be included at the array
+ * @param viewId The view that is active at the moment
+ */
+ @SuppressWarnings({
+ "rawtypes", "unchecked"
+ })
+ private void populateContributionList(IContributionItem[] itemsArray,
+ Collection<String> layoutNames, String viewId)
+ {
+ int i = 0;
+ for (String layoutName : layoutNames)
+ {
+ Map params = new HashMap();
+ params.put(IHandlerConstants.ACTIVE_VIEW_PARAMETER, viewId);
+
+ String id = EmulatorPlugin.PLUGIN_ID + ".layoutcmd." + layoutName;
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ CommandContributionItemParameter itemParam =
+ new CommandContributionItemParameter(window, id,
+ IHandlerConstants.CHANGE_EMULATOR_ORIENTATION_COMMAND, params, null,
+ null, null, layoutName, null, null,
+ CommandContributionItem.STYLE_RADIO, null, true);
+ itemsArray[i++] = new CommandContributionItem(itemParam);
+ }
+ }
+
+ /**
+ * Populates the array with a single disabled command, indicating that there
+ * are no layouts to choose
+ *
+ * @param itemsArray The array to be populated
+ */
+ @SuppressWarnings("rawtypes")
+ private void populateWithEmpty(IContributionItem[] itemsArray)
+ {
+ String id = EmulatorPlugin.PLUGIN_ID + ".emptylayout";
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ CommandContributionItemParameter itemParam =
+ new CommandContributionItemParameter(window, id,
+ IHandlerConstants.CHANGE_EMULATOR_ORIENTATION_COMMAND, new HashMap(), null,
+ null, null, EmulatorNLS.UI_LayoutContributionItem_NoLayoutsAvailable, null,
+ null, CommandContributionItem.STYLE_RADIO, null, false);
+ itemsArray[0] = new CommandContributionItem(itemParam);
+ }
+}
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/MainDisplayView.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/MainDisplayView.java
new file mode 100644
index 0000000..2280b21
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/MainDisplayView.java
@@ -0,0 +1,224 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator.ui.view;
+
+import static com.motorola.studio.android.common.log.StudioLogger.error;
+
+import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle;
+import org.eclipse.sequoyah.vnc.vncviewer.config.IPropertiesFileHandler;
+import org.eclipse.sequoyah.vnc.vncviewer.config.VNCConfiguration;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.ISWTPainter;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay;
+import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.img.SWTRemoteDisplayImg;
+import org.eclipse.sequoyah.vnc.vncviewer.network.VNCProtocolData;
+import org.eclipse.sequoyah.vnc.vncviewer.registry.VNCProtocolRegistry;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.commands.ICommandService;
+
+import com.motorola.studio.android.common.utilities.EclipseUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.core.exception.InstanceStopException;
+import com.motorola.studio.android.emulator.core.exception.SkinException;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
+import com.motorola.studio.android.emulator.core.skin.ISkinKeyXmlTags;
+import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
+import com.motorola.studio.android.emulator.ui.IUIHelpConstants;
+import com.motorola.studio.android.emulator.ui.controls.maindisplay.MainDisplayComposite;
+import com.motorola.studio.android.emulator.ui.handlers.IHandlerConstants;
+
+/**
+ * This class represents the view that shows the Main Display (without skin)
+ * of the emulator to the end user.
+ */
+public class MainDisplayView extends AbstractAndroidView
+{
+
+ /**
+ * The Android Emulator Main Display View view ID
+ */
+ public static final String EMULATOR_MAIN_DISPLAY_VIEW_ID =
+ EmulatorPlugin.PLUGIN_ID + ".mainDisplayView";
+
+ /**
+ * @see com.motorola.studio.android.emulator.ui.view.AbstractAndroidView#getViewId()
+ */
+ @Override
+ public String getViewId()
+ {
+ return EMULATOR_MAIN_DISPLAY_VIEW_ID;
+ }
+
+ /**
+ * @see com.motorola.studio.android.emulator.ui.view.AbstractAndroidView#getHelpId()
+ */
+ @Override
+ protected String getHelpId()
+ {
+ return IUIHelpConstants.EMULATOR_VIEW_MAIN_DISPLAY_HELP;
+ }
+
+ /**
+ * Creates the scrolled composite, the detached composite and the main
+ * display related to emulator.
+ *
+ * @param tab the tab item that will hold the graphical elements that
+ * represents the emulator
+ * @param instance the emulator instance
+ * @param emulatorData the object to be defined with the elements created.
+ */
+ @Override
+ protected void createWidgets(TabItem tab, final IAndroidEmulatorInstance instance,
+ final AndroidViewData tabData)
+ {
+ try
+ {
+ tabData.loadSkin(instance);
+ IAndroidSkin skin = tabData.getSkin();
+
+ ProtocolHandle handle = instance.getProtocolHandle();
+ VNCProtocolData data = VNCProtocolRegistry.getInstance().get(handle);
+ if (data != null)
+ {
+
+ int baseWidth =
+ skin.getSkinBean(instance.getCurrentLayout()).getSkinPropertyValue(
+ ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH);
+ int baseHeight =
+ skin.getSkinBean(instance.getCurrentLayout()).getSkinPropertyValue(
+ ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT);
+
+ ScrolledComposite scrolledComposite =
+ new ScrolledComposite(tab.getParent(), SWT.H_SCROLL | SWT.V_SCROLL
+ | SWT.BACKGROUND);
+
+ final MainDisplayComposite composite =
+ new MainDisplayComposite(scrolledComposite, SWT.BACKGROUND, baseWidth,
+ baseHeight, instance);
+ composite.setLayout(new FillLayout());
+
+ final SWTRemoteDisplay mainDisplay =
+ createMainDisplay(composite, skin, instance, (ISWTPainter) data
+ .getVncPainter());
+ composite.setSize(baseWidth, baseHeight);
+
+ scrolledComposite.setContent(composite);
+ tab.setControl(scrolledComposite);
+
+ scrolledComposite.addDisposeListener(new DisposeListener()
+ {
+ public void widgetDisposed(DisposeEvent e)
+ {
+ composite.dispose();
+ }
+ });
+
+ tabData.setCliDisplay(null);
+ tabData.setComposite(composite);
+ tabData.setMainDisplay(mainDisplay);
+ }
+ else
+ {
+ error("The protocol object set in the device instance is not supported. Stopping the emulator instance...");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.ERR_AndroidView_ProtocolImplementerNotSupported);
+
+ try
+ {
+ instance.stop(true);
+ }
+ catch (InstanceStopException e)
+ {
+ error("Error while running service for stopping virtual machine");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_General_CannotRunStopService);
+ }
+ }
+ }
+ catch (SkinException e)
+ {
+ error("The skin associated to this instance (" + instance.getName()
+ + ") is not installed or is corrupted.");
+ EclipseUtils.showErrorDialog(e);
+
+ try
+ {
+ instance.stop(true);
+ }
+ catch (InstanceStopException e1)
+ {
+ error("Error while running service for stopping virtual machine");
+ EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error,
+ EmulatorNLS.EXC_General_CannotRunStopService);
+ }
+ }
+
+ }
+
+ /**
+ * Creates the main display used by this instance
+ *
+ * @param parent The parent composite where to place the widgets
+ * @param skin The object containing skin information used to draw the widgets
+ * @param instance the emulator instance
+ * @param painter the painter used to draw the image
+ */
+ private SWTRemoteDisplay createMainDisplay(Composite parent, IAndroidSkin skin,
+ IAndroidEmulatorInstance instance, ISWTPainter painter)
+ {
+
+ // gets and reads the configuration files of the VNC session
+ IPropertiesFileHandler handler;
+ String configFile;
+
+ handler = IAndroidSkin.DEFAULT_PROPS_HANDLER;
+ configFile = IAndroidSkin.DEFAULT_VNC_CONFIG_FILE;
+
+ VNCConfiguration config = new VNCConfiguration(configFile, handler);
+
+ SWTRemoteDisplay mainDisplay =
+ new SWTRemoteDisplayImg(parent, config.getConfigurationProperties(), handler,
+ painter);
+ mainDisplay.setBackground(new Color(mainDisplay.getDisplay(), 0, 0, 0));
+ mainDisplay.setLayout(new FillLayout());
+
+ return mainDisplay;
+ }
+
+ /**
+ * Forces the refreshing of the menu elements.
+ */
+ @Override
+ protected void refreshMenuElements()
+ {
+ IViewSite viewSite = getViewSite();
+
+ // Update the radio button selection in the zoom menu
+ ICommandService service = (ICommandService) viewSite.getService(ICommandService.class);
+ service.refreshElements(IHandlerConstants.CHANGE_EMULATOR_ZOOM_COMMAND, null);
+ service.refreshElements(IHandlerConstants.CHANGE_EMULATOR_ORIENTATION_COMMAND, null);
+
+ }
+
+} \ No newline at end of file
diff --git a/src/plugins/emulator/src/com/motorola/studio/android/emulator10/StartAndroidEmulatorLogic.java b/src/plugins/emulator/src/com/motorola/studio/android/emulator10/StartAndroidEmulatorLogic.java
new file mode 100644
index 0000000..089dd06
--- /dev/null
+++ b/src/plugins/emulator/src/com/motorola/studio/android/emulator10/StartAndroidEmulatorLogic.java
@@ -0,0 +1,117 @@
+/*
+* Copyright (C) 2012 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.motorola.studio.android.emulator10;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.LinkedList;
+
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+
+import com.motorola.studio.android.adt.SdkUtils;
+import com.motorola.studio.android.common.utilities.PluginUtils;
+import com.motorola.studio.android.emulator.EmulatorPlugin;
+import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic;
+import com.motorola.studio.android.emulator.logic.ConnectVncLogic;
+import com.motorola.studio.android.emulator.logic.ForwardVncPortLogic;
+import com.motorola.studio.android.emulator.logic.IAndroidLogic;
+import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance;
+import com.motorola.studio.android.emulator.logic.StartEmulatorProcessLogic;
+import com.motorola.studio.android.emulator.logic.StartVncServerLogic;
+import com.motorola.studio.android.emulator.logic.TransferFilesLogic;
+
+public class StartAndroidEmulatorLogic extends AbstractStartAndroidEmulatorLogic
+{
+ @SuppressWarnings("incomplete-switch")
+ @Override
+ public Collection<IAndroidLogic> getLogicCollection(IAndroidLogicInstance instance,
+ LogicMode mode)
+ {
+ // When starting, all steps must be done. When restarting, only VNC server launch
+ // step will be performed.
+ Collection<IAndroidLogic> logicCollection = new LinkedList<IAndroidLogic>();
+
+ switch (mode)
+ {
+ case START_MODE:
+ logicCollection.add(new StartEmulatorProcessLogic());
+ break;
+ case TRANSFER_AND_CONNECT_VNC:
+ logicCollection.add(createTransferFilesLogic());
+ logicCollection.add(new ForwardVncPortLogic());
+ StartVncServerLogic startVncServerLogic = createStartVncServerLogic();
+ logicCollection.add(startVncServerLogic);
+ logicCollection.add(getConnectVncClientLogic(startVncServerLogic));
+ break;
+ case RESTART_VNC_SERVER:
+ logicCollection.add(createTransferFilesLogic());
+ logicCollection.add(new ForwardVncPortLogic());
+ logicCollection.add(createStartVncServerLogic());
+ break;
+ }
+
+ return logicCollection;
+ }
+
+ private String getResourceDir()
+ {
+ String resDir = "res";
+ if (SdkUtils.isOphoneSDK())
+ {
+ resDir = "res_OPhone";
+ }
+
+ return resDir;
+ }
+
+ protected IAndroidLogic createTransferFilesLogic()
+ {
+ File localDirParent = PluginUtils.getPluginInstallationPath(EmulatorPlugin.getDefault());
+ File localDir = new File(localDirParent, getResourceDir());
+
+ TransferFilesLogic transferLogic = new TransferFilesLogic();
+ transferLogic.setLocalDir(localDir.getAbsolutePath());
+ transferLogic.setRemoteDir("/data/local");
+ transferLogic.addFilename("fbvncserver");
+ return transferLogic;
+ }
+
+ protected StartVncServerLogic createStartVncServerLogic()
+ {
+ StartVncServerLogic logic = new StartVncServerLogic();
+ logic.addRemoteCommand("chmod 700 /data/local/fbvncserver");
+ logic.addRemoteCommand("/data/local/fbvncserver");
+ return logic;
+ }
+
+ protected IAndroidLogic getConnectVncClientLogic(StartVncServerLogic startVncServerLogic)
+ {
+ final ConnectVncLogic startVncClientLogic = new ConnectVncLogic();
+
+ startVncServerLogic.addVncServerJobListener(new JobChangeAdapter()
+ {
+ @Override
+ public void done(IJobChangeEvent ijobchangeevent)
+ {
+ startVncClientLogic.setVncServerDoneEvent(ijobchangeevent);
+ }
+ });
+
+ return startVncClientLogic;
+ }
+
+}