diff options
Diffstat (limited to 'src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AbstractAndroidView.java')
-rw-r--r-- | src/plugins/emulator/src/com/motorola/studio/android/emulator/ui/view/AbstractAndroidView.java | 1839 |
1 files changed, 1839 insertions, 0 deletions
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 |