1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.motorola.studio.android.emulator.ui.view; 17 18 import static com.motorola.studio.android.common.log.StudioLogger.debug; 19 import static com.motorola.studio.android.common.log.StudioLogger.error; 20 import static com.motorola.studio.android.common.log.StudioLogger.info; 21 22 import java.util.ArrayList; 23 import java.util.Collection; 24 import java.util.HashSet; 25 import java.util.LinkedHashMap; 26 import java.util.LinkedHashSet; 27 import java.util.List; 28 import java.util.Map; 29 import java.util.Set; 30 import java.util.concurrent.locks.Lock; 31 import java.util.concurrent.locks.ReentrantReadWriteLock; 32 33 import org.eclipse.core.runtime.IProgressMonitor; 34 import org.eclipse.core.runtime.IStatus; 35 import org.eclipse.core.runtime.NullProgressMonitor; 36 import org.eclipse.core.runtime.Platform; 37 import org.eclipse.core.runtime.Status; 38 import org.eclipse.core.runtime.jobs.ISchedulingRule; 39 import org.eclipse.core.runtime.jobs.Job; 40 import org.eclipse.jface.action.IContributionItem; 41 import org.eclipse.jface.action.IMenuListener; 42 import org.eclipse.jface.action.IMenuManager; 43 import org.eclipse.jface.action.IToolBarManager; 44 import org.eclipse.jface.action.MenuManager; 45 import org.eclipse.jface.dialogs.ProgressMonitorDialog; 46 import org.eclipse.jface.operation.IRunnableWithProgress; 47 import org.eclipse.osgi.util.NLS; 48 import org.eclipse.sequoyah.device.framework.model.IInstance; 49 import org.eclipse.sequoyah.vnc.protocol.PluginProtocolActionDelegate; 50 import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolHandle; 51 import org.eclipse.sequoyah.vnc.protocol.lib.ProtocolMessage; 52 import org.eclipse.sequoyah.vnc.vncviewer.graphics.IRemoteDisplay; 53 import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay; 54 import org.eclipse.swt.SWT; 55 import org.eclipse.swt.events.DisposeEvent; 56 import org.eclipse.swt.events.DisposeListener; 57 import org.eclipse.swt.events.FocusAdapter; 58 import org.eclipse.swt.events.FocusEvent; 59 import org.eclipse.swt.events.KeyListener; 60 import org.eclipse.swt.events.MouseEvent; 61 import org.eclipse.swt.events.MouseListener; 62 import org.eclipse.swt.events.MouseMoveListener; 63 import org.eclipse.swt.events.SelectionAdapter; 64 import org.eclipse.swt.events.SelectionEvent; 65 import org.eclipse.swt.graphics.GC; 66 import org.eclipse.swt.widgets.Canvas; 67 import org.eclipse.swt.widgets.Composite; 68 import org.eclipse.swt.widgets.Control; 69 import org.eclipse.swt.widgets.Event; 70 import org.eclipse.swt.widgets.Listener; 71 import org.eclipse.swt.widgets.Shell; 72 import org.eclipse.swt.widgets.TabFolder; 73 import org.eclipse.swt.widgets.TabItem; 74 import org.eclipse.ui.IActionBars; 75 import org.eclipse.ui.IPartListener2; 76 import org.eclipse.ui.IPerspectiveDescriptor; 77 import org.eclipse.ui.IPerspectiveListener; 78 import org.eclipse.ui.IViewPart; 79 import org.eclipse.ui.IViewSite; 80 import org.eclipse.ui.IWorkbench; 81 import org.eclipse.ui.IWorkbenchListener; 82 import org.eclipse.ui.IWorkbenchPage; 83 import org.eclipse.ui.IWorkbenchPartReference; 84 import org.eclipse.ui.PartInitException; 85 import org.eclipse.ui.PlatformUI; 86 import org.eclipse.ui.part.ViewPart; 87 88 import com.motorola.studio.android.common.preferences.DialogWithToggleUtils; 89 import com.motorola.studio.android.common.utilities.EclipseUtils; 90 import com.motorola.studio.android.common.utilities.PluginUtils; 91 import com.motorola.studio.android.emulator.EmulatorPlugin; 92 import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager; 93 import com.motorola.studio.android.emulator.core.exception.InstanceStopException; 94 import com.motorola.studio.android.emulator.core.exception.SkinException; 95 import com.motorola.studio.android.emulator.core.exception.StartCancelledException; 96 import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance; 97 import com.motorola.studio.android.emulator.core.model.IEmulatorView; 98 import com.motorola.studio.android.emulator.core.skin.IAndroidSkin; 99 import com.motorola.studio.android.emulator.i18n.EmulatorNLS; 100 import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic; 101 import com.motorola.studio.android.emulator.logic.AbstractStartAndroidEmulatorLogic.LogicMode; 102 import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance; 103 import com.motorola.studio.android.emulator.logic.StartVncServerLogic; 104 import com.motorola.studio.android.emulator.logic.stop.AndroidEmulatorStopper; 105 import com.motorola.studio.android.emulator.ui.IUIHelpConstants; 106 import com.motorola.studio.android.emulator.ui.controls.IAndroidComposite; 107 import com.motorola.studio.android.emulator.ui.controls.RemoteCLIDisplay; 108 import com.motorola.studio.android.emulator.ui.controls.nativewindow.NativeWindowComposite; 109 import com.motorola.studio.android.nativeos.IDevicePropertiesOSConstants; 110 import com.motorola.studio.android.nativeos.NativeUIUtils; 111 112 /** 113 * DESCRIPTION: 114 * This class represents the Android Emulator view. It provides the 115 * generic methods of the Emulator Views. The specific ones must be defined 116 * by the classes that extend it. 117 * 118 * RESPONSIBILITY: 119 * - Show the viewers to the end user 120 * 121 * COLABORATORS: 122 * None. 123 * 124 * USAGE: 125 * The public interface provides static and dynamic methods: 126 * STATIC METHODS: 127 * - Call getActiveInstance to retrieve the instance that corresponds to 128 * the emulator running at the active tab 129 * - Call updateActiveViewers to guarantee that the active viewer of all views 130 * is up to date in all emulator views opened, but do not make further verifications. 131 * DYNAMIC METHODS: 132 * - Call refreshView to updates all viewers including creation of viewers 133 * for started instances and removal of viewers 134 * - Call updateActiveViewer to guarantee that the active viewer is up to 135 * date in all emulator views opened, but do not make further verifications 136 */ 137 public abstract class AbstractAndroidView extends ViewPart implements IEmulatorView 138 { 139 private final MenuManager menuManager; 140 141 public static final String POPUP_MENU_ID = "com.motorola.studio.android.emulator.view.popup"; 142 143 private MouseListener mouseClickListener; 144 145 /** 146 * Preference key of the Question Dialog about stopping the emulators by closing view 147 */ 148 private static String STOP_BY_CLOSING_VIEW_KEY_PREFERENCE = "stop.by.closing.view"; 149 150 /** 151 * Preference key of the Question Dialog about displaying all emulators in the IDE 152 * 153 */ 154 private static String SHOW_EMULATOR_IN_THE_IDE_KEY_PREFERENCE = 155 "show.view.for.started.emulators"; 156 157 /** 158 * Preference key of the Question Dialog about stopping all emulators in shutdown 159 * 160 */ 161 private static String STOP_ALL_EMULATORS_IN_SHUTDOWN_KEY_PREFERENCE = 162 "stop.all.emulators.in.shutdown"; 163 164 /** 165 * All event types handled by the listeners in this class 166 */ 167 public static final int[] SWT_EVENT_TYPES = new int[] 168 { 169 SWT.KeyDown, SWT.KeyUp, SWT.MouseDown, SWT.MouseUp, SWT.MouseMove, SWT.MouseDoubleClick 170 }; 171 172 /** 173 * All possible Layout Operations 174 */ 175 public enum LayoutOpp 176 { 177 KEEP, NEXT 178 }; 179 180 /** 181 * Tab folder where to place each instance tab 182 */ 183 private TabFolder tabFolder; 184 185 Listener listener = new Listener() 186 { 187 public void handleEvent(Event event) 188 { 189 if (tabFolder.getItemCount() > 0) 190 { 191 TabItem activeTabItem = getActiveTabItem(); 192 193 if ((activeTabItem != null) && (activeTabItem.getControl() != null)) 194 { 195 info("Setting focus to Android Emulator " + activeTabItem.getData()); 196 activeTabItem.getControl().setFocus(); 197 } 198 } 199 } 200 }; 201 202 /** 203 * Map to collect the emulator AndroidViewData committed to its emulator instance. 204 */ 205 private final Map<IAndroidEmulatorInstance, AndroidViewData> instanceDataMap = 206 new LinkedHashMap<IAndroidEmulatorInstance, AndroidViewData>(); 207 208 /** 209 * Listener required to code the work-around for Sticky Views on perspectiveChanged method. 210 */ 211 private PerspectiveListenerImpl perspectiveListenerImpl; 212 213 /** 214 * Listener necessary to determine when the view is closed. 215 */ 216 private PartListenerImpl partListenerImpl; 217 218 /** 219 * Listener used to know if the view is being closed due to workbench shutdown. 220 */ 221 private static WorkbenchListenerImpl workbenchListenerImpl; 222 223 // the view is being closed during the Studio shutdown. 224 private boolean closingOnShutdown = false; 225 226 // Collection of the ids of the opened views that overwrite this class 227 private static final List<String> childrenIDs = new ArrayList<String>(); 228 229 // the instance being currently active at the emulator views 230 private static IAndroidEmulatorInstance activeInstance; 231 232 /** 233 * Listeners of tab switch event 234 */ 235 private static final Collection<Listener> tabSwitchListeners = new ArrayList<Listener>(); 236 237 /** 238 * Lock to assure that only the first thread will display the show view question 239 */ 240 private static Lock showViewLock = new ReentrantReadWriteLock().writeLock(); 241 242 /** 243 * Add a listener to be called when the tab selection changes 244 * 245 * @param listener the listener to be added 246 */ 247 public static void addTabSwitchListener(Listener listener) 248 { 249 tabSwitchListeners.add(listener); 250 } 251 252 /** 253 * Remove a listener that listen to tab switch events 254 * 255 * @param listener the listener to be removed 256 */ 257 public static void removeTabSwitchListener(Listener listener) 258 { 259 tabSwitchListeners.remove(listener); 260 } 261 262 /** 263 * Call listeners of tab switch events 264 */ 265 protected void handleTabSwitchEvent() 266 { 267 for (Listener listener : tabSwitchListeners) 268 { 269 listener.handleEvent(null); 270 } 271 } 272 273 /** 274 * Returns the View Identification. 275 * @return the unique ViewId 276 */ 277 protected abstract String getViewId(); 278 279 /** 280 * Creates the graphical elements representing the emulator that will be 281 * shown by the viewer in its tab item. 282 * 283 * @param tab the tab item that will hold the graphical elements that 284 * represents the emulator 285 * @param instance the emulator instance 286 * @param emulatorData the object to be defined with the elements created. 287 * @throws SkinException if the AVD configured skin does not exists 288 */ 289 protected abstract void createWidgets(TabItem tab, final IAndroidEmulatorInstance instance, 290 final AndroidViewData emulatorData) throws SkinException; 291 292 /** 293 * Forces the refreshing of the menu elements. 294 */ 295 protected abstract void refreshMenuElements(); 296 297 /** 298 * Retrieves the instance being currently active at the emulator views. 299 * 300 * @return The active instance, or null if there is no active instance 301 */ 302 public static IAndroidEmulatorInstance getActiveInstance() 303 { 304 return activeInstance; 305 } 306 307 /** 308 * Retrieves the instance being currently active at the emulator views. 309 * 310 * @return The active instance, or null if there is no active instance 311 */ 312 public static void setInstance(IAndroidEmulatorInstance emulatorInstance) 313 { 314 if (!childrenIDs.isEmpty()) 315 { 316 AbstractAndroidView view = 317 (AbstractAndroidView) EclipseUtils.getActiveView(childrenIDs.get(0)); 318 if (view != null) 319 { 320 view.setActiveInstanceId(emulatorInstance.getInstanceIdentifier()); 321 activeInstance = emulatorInstance; 322 } 323 } 324 } 325 326 /** 327 * Retrieves the information about the viewer used to display the given emulator instance 328 * and returns null if there is no AndroidViewData for the given emulator instance. 329 * viewer. 330 * @param the emulator instance whose Android Viewer data need to be retrieved. 331 * @return the AndroidViewerData for the given Emulator instance (null if none is available). 332 */ 333 public AndroidViewData getViewData(IAndroidEmulatorInstance instance) 334 { 335 AndroidViewData viewData = instanceDataMap.get(instance); 336 return viewData; 337 } 338 339 public IAndroidSkin getSkin(IAndroidEmulatorInstance instance) 340 { 341 IAndroidSkin skin = null; 342 AndroidViewData viewData = getViewData(instance); 343 if (viewData != null) 344 { 345 skin = viewData.getSkin(); 346 } 347 return skin; 348 } 349 350 /** 351 * Gets the layout to set, if opp is NEXT or PREVIOUS 352 * 353 * @param viewId The view that is currently active 354 * @param opp The layout operation to perform 355 */ 356 public static String getPreviousOrNextLayout(String viewId, LayoutOpp opp) 357 { 358 String prevNextLayout = null; 359 AbstractAndroidView view = (AbstractAndroidView) EclipseUtils.getActiveView(viewId); 360 if (view != null) 361 { 362 prevNextLayout = view.getPreviousOrNextLayout(opp); 363 } 364 365 return prevNextLayout; 366 } 367 368 /** 369 * Gets the layout to set, if opp is NEXT or PREVIOUS 370 * 371 * @param opp The layout operation to perform 372 */ 373 @SuppressWarnings("incomplete-switch") 374 private String getPreviousOrNextLayout(LayoutOpp opp) 375 { 376 String prevNextLayout = null; 377 if (activeInstance != null) 378 { 379 String referenceLayout = activeInstance.getCurrentLayout(); 380 AndroidViewData viewData = instanceDataMap.get(activeInstance); 381 if (viewData != null) 382 { 383 IAndroidComposite androidComposite = viewData.getComposite(); 384 if ((androidComposite != null)) 385 { 386 IAndroidSkin androidSkin = viewData.getSkin(); 387 388 if (androidSkin != null) 389 { 390 switch (opp) 391 { 392 case NEXT: 393 prevNextLayout = androidSkin.getNextLayout(referenceLayout); 394 break; 395 } 396 } 397 } 398 } 399 } 400 401 return prevNextLayout; 402 } 403 404 /** 405 * Updates the zoom action that needs to be checked in all emulator views. 406 * This method must be called every time the focus changes to another 407 * viewer. 408 * 409 * @param layoutName The layout name to set 410 */ 411 public static void changeLayout(String layoutName) 412 { 413 for (String viewId : childrenIDs) 414 { 415 AbstractAndroidView view = (AbstractAndroidView) EclipseUtils.getActiveView(viewId); 416 if (view != null) 417 { 418 view.updateActiveViewer(layoutName); 419 } 420 } 421 } 422 423 /** 424 * Gets the help ID to be used for attaching 425 * context sensitive help. 426 * 427 * Classes that extends this class and want to set 428 * their on help should override this method 429 */ 430 protected String getHelpId() 431 { 432 return IUIHelpConstants.EMULATOR_VIEW_HELP; 433 } 434 435 /** 436 * @see org.eclipse.ui.IWorkbenchPart#createPartControl(Composite) 437 */ 438 @Override 439 public void createPartControl(Composite parent) 440 { 441 PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, getHelpId()); 442 443 this.tabFolder = new TabFolder(parent, SWT.BORDER | SWT.BACKGROUND); 444 IViewSite viewSite = getViewSite(); 445 446 // Add listeners 447 tabFolder.addSelectionListener(new SelectionAdapter() 448 { 449 @Override 450 public void widgetSelected(SelectionEvent event) 451 { 452 setActiveInstanceId(); 453 updateMenuAndToolbars(); 454 handleTabSwitchEvent(); 455 } 456 }); 457 458 tabFolder.addFocusListener(new FocusAdapter() 459 { 460 461 @Override 462 public void focusGained(FocusEvent e) 463 { 464 handleTabSwitchEvent(); 465 } 466 467 }); 468 469 perspectiveListenerImpl = new PerspectiveListenerImpl(); 470 viewSite.getWorkbenchWindow().addPerspectiveListener(perspectiveListenerImpl); 471 472 partListenerImpl = new PartListenerImpl(); 473 viewSite.getPage().addPartListener(partListenerImpl); 474 475 if (workbenchListenerImpl == null) 476 { 477 workbenchListenerImpl = new WorkbenchListenerImpl(); 478 viewSite.getWorkbenchWindow().getWorkbench() 479 .addWorkbenchListener(workbenchListenerImpl); 480 } 481 482 // Update UI 483 refreshView(); 484 485 IActionBars actionBars = viewSite.getActionBars(); 486 if (actionBars != null) 487 { 488 IMenuManager menuManager = actionBars.getMenuManager(); 489 if (menuManager != null) 490 { 491 menuManager.addMenuListener(new IMenuListener() 492 { 493 public void menuAboutToShow(IMenuManager manager) 494 { 495 // Calls the manager update method to guarantee that the command have its handler 496 // initialized. Otherwise, the next command will not work properly 497 if (manager != null) 498 { 499 manager.update(true); 500 } 501 updateMenuAndToolbars(); 502 } 503 }); 504 } 505 } 506 507 //register the popup menu 508 viewSite.registerContextMenu(POPUP_MENU_ID, menuManager, null); 509 510 //create listener 511 if (Platform.getOS().contains(Platform.OS_MACOSX)) 512 { 513 mouseClickListener = new MouseListener() 514 { 515 516 public void mouseDoubleClick(MouseEvent e) 517 { 518 //do nothing 519 } 520 521 public void mouseDown(MouseEvent e) 522 { 523 if ((e.button == 1) && (e.stateMask == SWT.CONTROL)) 524 { 525 menuManager.getMenu().setVisible(true); 526 } 527 } 528 529 public void mouseUp(MouseEvent e) 530 { 531 //do nothing 532 } 533 534 }; 535 } 536 else 537 { 538 mouseClickListener = new MouseListener() 539 { 540 541 public void mouseDoubleClick(MouseEvent e) 542 { 543 //do nothing 544 } 545 546 public void mouseDown(MouseEvent e) 547 { 548 if (e.button == 3) 549 { 550 menuManager.getMenu().setVisible(true); 551 } 552 } 553 554 public void mouseUp(MouseEvent e) 555 { 556 //do nothing 557 } 558 559 }; 560 561 } 562 563 } 564 565 /** 566 * Constructor default 567 */ 568 public AbstractAndroidView() 569 { 570 childrenIDs.add(getViewId()); 571 menuManager = new MenuManager("", POPUP_MENU_ID); 572 addTabSwitchListener(listener); 573 } 574 575 /** 576 * @see org.eclipse.ui.IWorkbenchPart#setFocus() 577 */ 578 @Override 579 public void setFocus() 580 { 581 if (tabFolder.getItemCount() > 0) 582 { 583 TabItem activeTabItem = getActiveTabItem(); 584 585 if ((activeTabItem != null) && (activeTabItem.getControl() != null)) 586 { 587 info("Setting focus to Android Emulator " + activeTabItem.getData()); 588 activeTabItem.getControl().setFocus(); 589 } 590 else 591 { 592 info("Setting focus to Android Emulator View"); 593 tabFolder.setFocus(); 594 } 595 } 596 else 597 { 598 info("Setting focus to Android Emulator View"); 599 tabFolder.setFocus(); 600 } 601 602 updateMenuAndToolbars(); 603 } 604 605 /** 606 * @see org.eclipse.ui.IWorkbenchPart#dispose() 607 */ 608 @Override 609 public void dispose() 610 { 611 removeTabSwitchListener(listener); 612 debug("Disposing View: " + getClass()); 613 getViewSite().getWorkbenchWindow().removePerspectiveListener(perspectiveListenerImpl); 614 getViewSite().getPage().removePartListener(partListenerImpl); 615 perspectiveListenerImpl = null; 616 partListenerImpl = null; 617 instanceDataMap.clear(); 618 tabFolder.dispose(); 619 childrenIDs.remove(getViewId()); 620 super.dispose(); 621 } 622 623 /** 624 * This method rebuilds the skin, adding a new tab in the Android Emulator View 625 * to show it. 626 * 627 * It should be used when the Android Emulator view is being created when the Android Emulator 628 * instance is not stopped. 629 */ 630 public void refreshView() 631 { 632 Job refreshViews = new Job("Refresh Emulator View") 633 { 634 @Override 635 protected IStatus run(IProgressMonitor monitor) 636 { 637 638 info("Updating Android Emulator viewers"); 639 640 final DeviceFrameworkManager framework = DeviceFrameworkManager.getInstance(); 641 642 Collection<IAndroidEmulatorInstance> startedInstances = 643 framework.getAllStartedInstances(); 644 645 for (final IAndroidEmulatorInstance instance : startedInstances) 646 { 647 if (instance 648 .getProperties() 649 .getProperty(IDevicePropertiesOSConstants.useVnc, 650 NativeUIUtils.getDefaultUseVnc()).equals("true")) 651 { 652 if (!instance.isConnected()) 653 { 654 IStatus returnStatus = null; 655 returnStatus = connectVNC(instance, monitor); 656 if (returnStatus.isOK()) 657 { 658 PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() 659 { 660 public void run() 661 { 662 createViewer(instance); 663 } 664 }); 665 } 666 } 667 } 668 } 669 670 PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() 671 { 672 public void run() 673 { 674 675 Collection<IAndroidEmulatorInstance> connectedInstances = 676 framework.getAllConnectedInstances(); 677 678 Collection<IAndroidEmulatorInstance> instancesWithViewerCollection = 679 getInstancesWithViewer(); 680 681 for (IAndroidEmulatorInstance instance : connectedInstances) 682 { 683 if (!instancesWithViewerCollection.contains(instance)) 684 { 685 createViewer(instance); 686 } 687 else 688 { 689 // update the collection for removing the stopped instances 690 instancesWithViewerCollection.remove(instance); 691 } 692 } 693 694 // Remove not started instances from viewer 695 for (IAndroidEmulatorInstance instance : instancesWithViewerCollection) 696 { 697 disposeViewer(instance); 698 info("Disposed viewer of " + instance); 699 } 700 701 // Update the active instance variable after any creation/disposal is 702 // made. Update the active viewer only if the active viewer is new 703 activeInstance = getActiveInstanceFromCurrentView(); 704 if (activeInstance != null) 705 { 706 setActiveInstanceId(); 707 handleTabSwitchEvent(); 708 } 709 710 updateMenuAndToolbars(); 711 } 712 713 }); 714 715 return Status.OK_STATUS; 716 } 717 }; 718 refreshViews.setRule(new RefreshRule()); 719 refreshViews.schedule(); 720 } 721 722 class RefreshRule implements ISchedulingRule 723 { 724 public boolean contains(ISchedulingRule rule) 725 { 726 return this == rule; 727 } 728 729 public boolean isConflicting(ISchedulingRule rule) 730 { 731 return rule instanceof RefreshRule; 732 } 733 } 734 735 /** 736 * Updates the zoom action that needs to be checked. 737 * This method must be called every time the focus changes to another 738 * viewer. 739 */ 740 public void updateActiveViewer() 741 { 742 updateActiveViewer(null); 743 } 744 745 /** 746 * Updates the zoom action that needs to be checked, after performing a layout operation 747 * 748 * @param layoutName The name of the layout to set if opp is SETLAYOUT 749 */ 750 public void updateActiveViewer(String layoutName) 751 { 752 info("Updating Android Emulator view"); 753 754 if (activeInstance != null) 755 { 756 AndroidViewData viewData = instanceDataMap.get(activeInstance); 757 if (viewData != null) 758 { 759 IAndroidComposite androidComposite = viewData.getComposite(); 760 if ((androidComposite != null)) 761 { 762 if ((activeInstance.getProperties().getProperty( 763 IDevicePropertiesOSConstants.useVnc, NativeUIUtils.getDefaultUseVnc())) 764 .equals("true")) 765 { 766 IAndroidSkin androidSkin = viewData.getSkin(); 767 768 if (androidSkin != null) 769 { 770 if (layoutName != null) 771 { 772 activeInstance.setCurrentLayout(layoutName); 773 } 774 775 boolean isNeeded = 776 androidSkin.isSwapWidthHeightNeededAtLayout(activeInstance 777 .getCurrentLayout()); 778 IRemoteDisplay.Rotation rotation = 779 (isNeeded 780 ? IRemoteDisplay.Rotation.ROTATION_90DEG_COUNTERCLOCKWISE 781 : IRemoteDisplay.Rotation.ROTATION_0DEG); 782 viewData.getMainDisplay().setRotation(rotation); 783 androidComposite.applyLayout(activeInstance.getCurrentLayout()); 784 } 785 } 786 androidComposite.applyZoomFactor(); 787 } 788 789 } 790 } 791 792 updateMenuAndToolbars(); 793 794 info("Updated Android Emulator view"); 795 } 796 797 public void changeToNextLayout() 798 { 799 AndroidViewData viewData = instanceDataMap.get(activeInstance); 800 IAndroidComposite androidComposite = viewData.getComposite(); 801 if (androidComposite instanceof NativeWindowComposite) 802 { 803 ((NativeWindowComposite) androidComposite).changeToNextLayout(); 804 } 805 } 806 807 /** 808 * Retrieves the instance being currently displayed at this view. 809 * 810 * @return The active instance, or null if there is no active instance 811 */ 812 private IAndroidEmulatorInstance getActiveInstanceFromCurrentView() 813 { 814 TabItem activeInstanceItem = getActiveTabItem(); 815 IAndroidEmulatorInstance instance = null; 816 if (activeInstanceItem != null) 817 { 818 instance = (IAndroidEmulatorInstance) (activeInstanceItem.getData()); 819 } 820 else 821 { 822 debug("No active instance being shown at emulator view"); 823 } 824 825 return instance; 826 } 827 828 /** 829 * Executes the procedure to connect to the VNC 830 * 831 * @param androidDevice 832 * The device being connected 833 */ 834 public IStatus connectVNC(final IAndroidEmulatorInstance instance, IProgressMonitor monitor) 835 { 836 IStatus statusToReturn = Status.OK_STATUS; 837 838 try 839 { 840 IAndroidLogicInstance logicInstance = (IAndroidLogicInstance) instance; 841 AbstractStartAndroidEmulatorLogic startLogic = logicInstance.getStartLogic(); 842 843 startLogic.execute(logicInstance, LogicMode.TRANSFER_AND_CONNECT_VNC, 844 logicInstance.getTimeout(), monitor); 845 } 846 catch (StartCancelledException e1) 847 { 848 info("The user canceled the transfer/connect to VNC phase."); 849 statusToReturn = Status.CANCEL_STATUS; 850 } 851 catch (Exception e1) 852 { 853 error("Could not establish VNC Connection to " + instance); 854 statusToReturn = 855 new Status(IStatus.ERROR, EmulatorPlugin.PLUGIN_ID, NLS.bind( 856 EmulatorNLS.ERR_CannotConnectToVNC, instance.getName())); 857 } 858 return statusToReturn; 859 } 860 861 /** 862 * Shows the Android Emulator view, if not being shown 863 */ 864 public static void showView() 865 { 866 info("Open and move focus to the emulator view"); 867 868 boolean emulatorViewOpened = 869 !EclipseUtils.getAllOpenedViewsWithId(AndroidView.ANDROID_VIEW_ID).isEmpty(); 870 871 try 872 { 873 // if emulator view is opened previously or if no emulator view is opened, 874 // show / refresh the emulator view. 875 if (emulatorViewOpened) 876 { 877 EclipseUtils.showView(AndroidView.ANDROID_VIEW_ID); 878 } 879 else 880 { 881 // Make sure only one open view (due to the transition to online) will occur at the same time. 882 // e.g. if the "question open dialog" is already opened, it is not needed one 883 884 if (showViewLock.tryLock()) 885 { 886 try 887 { 888 889 boolean openEmulatorView = 890 DialogWithToggleUtils 891 .showQuestion( 892 SHOW_EMULATOR_IN_THE_IDE_KEY_PREFERENCE, 893 EmulatorNLS.QUESTION_AbstractAndroidView_OpenViewForStartedEmulatorsTitle, 894 EmulatorNLS.QUESTION_AbstractAndroidView_OpenViewForStartedEmulatorsMessage); 895 if (openEmulatorView) 896 { 897 EclipseUtils.showView(AndroidView.ANDROID_VIEW_ID); 898 } 899 } 900 finally 901 { 902 showViewLock.unlock(); 903 } 904 } 905 } 906 } 907 catch (PartInitException e) 908 { 909 error("The Android Emulator View could not be opened programatically"); 910 EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error, 911 EmulatorNLS.EXC_AbstractAndroidView_ViewNotAccessibleProgramatically); 912 } 913 } 914 915 /** 916 * Creates a viewer for the provided instance 917 * 918 * @param instance The instance that will have a viewer created at this view 919 */ 920 private void createViewer(final IAndroidEmulatorInstance instance) 921 { 922 if (instance != null) 923 { 924 info("Creating tab for " + instance + " on " + getClass()); 925 926 Set<IAndroidEmulatorInstance> currentInstancesWithTab = 927 getInstancesWithAtLeastOneViewer(); 928 929 // Creates a tab item to hold the skin at the view 930 TabItem newTabItem = new TabItem(tabFolder, SWT.NONE); 931 932 // Set parameters at the tab item 933 newTabItem.setText(instance.getFullName()); 934 newTabItem.setData(instance); 935 AndroidViewData emulatorData = new AndroidViewData(); 936 instanceDataMap.put(instance, emulatorData); 937 938 try 939 { 940 createWidgets(newTabItem, instance, emulatorData); 941 tabFolder.setSelection(newTabItem); 942 setActiveInstanceId(); 943 944 //add popup menu 945 if (newTabItem.getControl() != null) 946 { 947 menuManager.createContextMenu(newTabItem.getControl()); 948 newTabItem.getControl().addMouseListener(mouseClickListener); 949 } 950 951 ProtocolMessage setEncodingMsg = new ProtocolMessage(2); 952 setEncodingMsg.setFieldValue("padding", 0); 953 setEncodingMsg.setFieldValue("number-of-encodings", 1); 954 setEncodingMsg.setFieldValue("encoding-type", "encoding-types", 0, 0); 955 PluginProtocolActionDelegate.sendMessageToServer(instance.getProtocolHandle(), 956 setEncodingMsg); 957 958 info("Created tab for " + instance); 959 960 if (instance 961 .getProperties() 962 .getProperty(IDevicePropertiesOSConstants.useVnc, 963 NativeUIUtils.getDefaultUseVnc()).toString().equals("true")) 964 { 965 startVncDisplays(instance); 966 info("Started displays for " + instance); 967 968 // overwrite original tml listeners 969 addListenersToMainDisplay(emulatorData); 970 } 971 else 972 { 973 IAndroidComposite parentComposite = emulatorData.getComposite(); 974 ((NativeWindowComposite) parentComposite).addMouseListener(mouseClickListener); 975 } 976 977 IAndroidComposite androidComposite = emulatorData.getComposite(); 978 if (androidComposite != null) 979 { 980 androidComposite.applyZoomFactor(); 981 } 982 983 // If this is the first view to be opened, guarantee that the screen orientation is 984 // synchronized with the current layout (only when using VNC) 985 if (!currentInstancesWithTab.contains(instance) 986 && instance 987 .getProperties() 988 .getProperty(IDevicePropertiesOSConstants.useVnc, 989 NativeUIUtils.getDefaultUseVnc()).toString().equals("true")) 990 { 991 IAndroidSkin skin = getSkin(instance); 992 if (skin != null) 993 { 994 instance.changeOrientation(skin.getLayoutScreenCommand(instance 995 .getCurrentLayout())); 996 } 997 } 998 999 updateActiveViewer(); 1000 1001 info("Created tab for Android Emulator " + instance); 1002 } 1003 catch (SkinException e) 1004 { 1005 error("The skin associated to this instance (" + instance.getName() 1006 + ") is not installed or is corrupted."); 1007 EclipseUtils.showErrorDialog(e); 1008 1009 try 1010 { 1011 instance.stop(true); 1012 disposeViewer(instance); 1013 } 1014 catch (InstanceStopException e1) 1015 { 1016 error("Error while running service for stopping virtual machine"); 1017 EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error, 1018 EmulatorNLS.EXC_General_CannotRunStopService); 1019 } 1020 } 1021 } 1022 } 1023 1024 private void addListenersToMainDisplay(AndroidViewData emulatorData) 1025 { 1026 // TmL registers listeners during start, and unregisters all of them during 1027 // stop. To adapt the listeners to Studio needs, we are including the following 1028 // operations after the start display call from TmL. With this we are achieving: 1029 // 1030 // 1. TmL registers several listeners at its canvas (TmL start method) 1031 // 2. Studio unregisters the TmL listeners (this method) 1032 // 3. Studio registers new listeners to replace the TmL ones (this method) 1033 // 4. TmL unregisters Studio listeners instead of its own (TmL stop method) 1034 1035 SWTRemoteDisplay remoteDisplay = emulatorData.getMainDisplay(); 1036 final Canvas canvas = remoteDisplay.getCanvas(); 1037 IAndroidComposite parentComposite = emulatorData.getComposite(); 1038 1039 for (int eventType : SWT_EVENT_TYPES) 1040 { 1041 for (Listener listener : canvas.getListeners(eventType)) 1042 { 1043 canvas.removeListener(eventType, listener); 1044 } 1045 } 1046 1047 KeyListener keyListener = parentComposite.getKeyListener(); 1048 final MouseListener mouseListener = parentComposite.getMouseListener(); 1049 MouseMoveListener mouseMoveListener = parentComposite.getMouseMoveListener(); 1050 1051 canvas.addKeyListener(keyListener); 1052 canvas.addMouseListener(mouseListener); 1053 canvas.addMouseMoveListener(mouseMoveListener); 1054 1055 // Due to the differences in listener registration between TmL and Studio, it will 1056 // remain a registered listener when the viewer is disposed. For this reason, the 1057 // following dispose listener is being registered. 1058 DisposeListener disposeListener = new DisposeListener() 1059 { 1060 public void widgetDisposed(DisposeEvent arg0) 1061 { 1062 canvas.removeMouseListener(mouseListener); 1063 canvas.removeMouseListener(mouseClickListener); 1064 } 1065 }; 1066 emulatorData.setDisposeListener(disposeListener); 1067 canvas.addDisposeListener(disposeListener); 1068 canvas.addMouseListener(mouseClickListener); 1069 } 1070 1071 /** 1072 * Disposes the viewer of the provided instance 1073 * 1074 * @param instance The instance that will have a viewer disposed from this view 1075 */ 1076 private void disposeViewer(final IAndroidEmulatorInstance instance) 1077 { 1078 info("Disposing tab of Android Emulator at " + instance); 1079 1080 TabItem item = getTabItem(instance); 1081 if (item != null) 1082 { 1083 1084 stopVncDisplays(instance); 1085 1086 //if there are no other viewers, we can stop protocol and vnc server 1087 if ((childrenIDs.size() == 1) 1088 && (instance 1089 .getProperties() 1090 .getProperty(IDevicePropertiesOSConstants.useVnc, 1091 NativeUIUtils.getDefaultUseVnc()).toString().equals("true"))) 1092 { 1093 info("There is only one view opened, stop VNC protocol and VNC Server"); 1094 stopVncProtocol((IAndroidLogicInstance) instance); 1095 stopVncServer(instance); 1096 } 1097 1098 AndroidViewData data = instanceDataMap.get(instance); 1099 if (data != null) 1100 { 1101 SWTRemoteDisplay mainDisplay = data.getMainDisplay(); 1102 if (mainDisplay != null) 1103 { 1104 Canvas canvas = mainDisplay.getCanvas(); 1105 1106 if (canvas != null) 1107 { 1108 canvas.removeDisposeListener(data.getDisposeListener()); 1109 } 1110 } 1111 } 1112 1113 Control c = item.getControl(); 1114 if (c != null) 1115 { 1116 c.dispose(); 1117 } 1118 item.setControl(null); 1119 1120 item.dispose(); 1121 instanceDataMap.remove(instance); 1122 updateMenuAndToolbars(); 1123 info("Disposed tab of Android Emulator at " + instance); 1124 } 1125 1126 } 1127 1128 /** 1129 * Gets the list of instances with viewers associated. 1130 * @return the collection of instances 1131 */ 1132 private Collection<IAndroidEmulatorInstance> getInstancesWithViewer() 1133 { 1134 final Collection<IAndroidEmulatorInstance> instancesWithViewer = 1135 new LinkedHashSet<IAndroidEmulatorInstance>(); 1136 1137 if (!tabFolder.isDisposed()) 1138 { 1139 final TabItem[] allItems = tabFolder.getItems(); 1140 1141 for (TabItem item : allItems) 1142 { 1143 if (!item.isDisposed()) 1144 { 1145 instancesWithViewer.add((IAndroidEmulatorInstance) item.getData()); 1146 } 1147 } 1148 } 1149 1150 return instancesWithViewer; 1151 } 1152 1153 private static Set<IAndroidEmulatorInstance> getInstancesWithAtLeastOneViewer() 1154 { 1155 Set<IAndroidEmulatorInstance> instancesSet = new HashSet<IAndroidEmulatorInstance>(); 1156 for (String viewId : childrenIDs) 1157 { 1158 AbstractAndroidView view = (AbstractAndroidView) EclipseUtils.getActiveView(viewId); 1159 if (view != null) 1160 { 1161 instancesSet.addAll(view.getInstancesWithViewer()); 1162 } 1163 } 1164 1165 return instancesSet; 1166 } 1167 1168 /** 1169 * Gets the tab item related to the instance 1170 * @param instance the emulator instance 1171 * @return the tab item 1172 */ 1173 private TabItem getTabItem(IAndroidEmulatorInstance instance) 1174 { 1175 TabItem result = null; 1176 if (!tabFolder.isDisposed()) 1177 { 1178 TabItem[] allItems = tabFolder.getItems(); 1179 1180 for (TabItem item : allItems) 1181 { 1182 if (instance.equals(item.getData())) 1183 { 1184 result = item; 1185 break; 1186 } 1187 } 1188 } 1189 1190 return result; 1191 } 1192 1193 /** 1194 * Gets the tab item related to the instance 1195 * @param instance the emulator instance 1196 * @return the tab item 1197 */ 1198 private TabItem getTabItem(IInstance instance) 1199 { 1200 TabItem result = null; 1201 if (!tabFolder.isDisposed()) 1202 { 1203 TabItem[] allItems = tabFolder.getItems(); 1204 1205 for (TabItem item : allItems) 1206 { 1207 if (instance.getName() 1208 .equals(((IAndroidEmulatorInstance) item.getData()).getName())) 1209 { 1210 result = item; 1211 break; 1212 } 1213 } 1214 } 1215 1216 return result; 1217 } 1218 1219 /** 1220 * Retrieves the active tab at view 1221 * 1222 * @return The active tab, or null of there is no tab at tab folder 1223 */ 1224 private TabItem getActiveTabItem() 1225 { 1226 int activeInstanceIndex = this.tabFolder.getSelectionIndex(); 1227 1228 TabItem activeTabItem = null; 1229 1230 if (activeInstanceIndex >= 0) 1231 { 1232 activeTabItem = this.tabFolder.getItem(activeInstanceIndex); 1233 } 1234 1235 return activeTabItem; 1236 } 1237 1238 /** 1239 * Updates the zoom action that needs to be checked. 1240 * This method must be called every time the focus changes to another 1241 * viewer 1242 */ 1243 private void updateMenuAndToolbars() 1244 { 1245 IViewSite viewSite = getViewSite(); 1246 1247 if (viewSite != null) 1248 { 1249 IActionBars actionBars = viewSite.getActionBars(); 1250 1251 if (actionBars != null) 1252 { 1253 IMenuManager menuManager = actionBars.getMenuManager(); 1254 updateMenuManager(menuManager, viewSite); 1255 1256 IToolBarManager toolbarManager = actionBars.getToolBarManager(); 1257 if (toolbarManager != null) 1258 { 1259 IContributionItem[] items = toolbarManager.getItems(); 1260 for (IContributionItem item : items) 1261 { 1262 item.update(); 1263 } 1264 } 1265 } 1266 1267 refreshMenuElements(); 1268 } 1269 } 1270 1271 /** 1272 * Recursive method to update items at menus. The recursion helps to update submenus 1273 * 1274 * @param manager The manager that holds a menu items 1275 * @param viewSite The current view site. 1276 */ 1277 private void updateMenuManager(IMenuManager manager, IViewSite viewSite) 1278 { 1279 // Update the items in menu manager 1280 if (manager != null) 1281 { 1282 IContributionItem[] items = manager.getItems(); 1283 for (IContributionItem item : items) 1284 { 1285 if (item instanceof IMenuManager) 1286 { 1287 updateMenuManager((IMenuManager) item, viewSite); 1288 } 1289 else 1290 { 1291 item.update(); 1292 } 1293 } 1294 } 1295 } 1296 1297 /** 1298 * Stops all emulator instances with the Progress Monitor opened. 1299 */ 1300 private void stopEmulatorInstances() 1301 { 1302 // defines the runnable object for stopping emulator instances. 1303 final IRunnableWithProgress stopRunnable = new IRunnableWithProgress() 1304 { 1305 public void run(IProgressMonitor monitor) 1306 { 1307 Collection<IAndroidEmulatorInstance> startedInstances = 1308 DeviceFrameworkManager.getInstance().getAllStartedInstances(); 1309 boolean errorsHappened = false; 1310 1311 for (IAndroidEmulatorInstance instance : startedInstances) 1312 { 1313 try 1314 { 1315 instance.stop(true); 1316 } 1317 catch (InstanceStopException e) 1318 { 1319 errorsHappened = true; 1320 } 1321 } 1322 1323 // if closing on shutdown, use a progress bar and stall UI 1324 if (closingOnShutdown) 1325 { 1326 // start a progress monitor 1327 monitor.beginTask("", IProgressMonitor.UNKNOWN); 1328 1329 // make sure the stop instance job finished 1330 Job[] jobs = Job.getJobManager().find(null); // get all jobs 1331 for (Job job : jobs) 1332 { 1333 if (job.getName() 1334 .equals(EmulatorNLS.UI_AbstractAndroidView_StopInstanceJob)) 1335 { 1336 // when job result is not null, it has finished 1337 while (job.getResult() == null) 1338 { 1339 try 1340 { 1341 // sleep a little so the waiting is not too busy 1342 Thread.sleep(1000); 1343 } 1344 catch (InterruptedException e) 1345 { 1346 // do nothing 1347 } 1348 } 1349 } 1350 } 1351 } 1352 1353 if (errorsHappened) 1354 { 1355 EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error, 1356 EmulatorNLS.EXC_AncroidView_CannotRunMultipleStopServices); 1357 } 1358 1359 } 1360 }; 1361 1362 // executes the runnable defined above. 1363 PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() 1364 { 1365 public void run() 1366 { 1367 Shell currentShell = getViewSite().getShell(); 1368 ProgressMonitorDialog dialog = new ProgressMonitorDialog(currentShell); 1369 try 1370 { 1371 dialog.run(true, false, stopRunnable); 1372 } 1373 catch (Exception e) 1374 { 1375 // Should not have exceptions. 1376 // The runnable is not interrupted and it handles exceptions internally 1377 // Log runtime exceptions 1378 error("Runtime exception was thrown: " + e.getClass().getSimpleName()); 1379 } 1380 } 1381 }); 1382 } 1383 1384 /** 1385 * Sets the identifier of the instance being currently displayed at view 1386 */ 1387 private void setActiveInstanceId(String activeHost) 1388 { 1389 for (String viewId : childrenIDs) 1390 { 1391 Collection<IViewPart> viewsToUpdateMenu = EclipseUtils.getAllOpenedViewsWithId(viewId); 1392 for (IViewPart view : viewsToUpdateMenu) 1393 { 1394 AbstractAndroidView emulatorView = (AbstractAndroidView) view; 1395 emulatorView.setSelection(activeHost); 1396 } 1397 1398 } 1399 } 1400 1401 /** 1402 * Sets the identifier of the instance being currently displayed at view 1403 */ 1404 private void setActiveInstanceId() 1405 { 1406 TabItem activeInstanceItem = getActiveTabItem(); 1407 if ((activeInstanceItem != null) && (activeInstanceItem.getData() != null)) 1408 { 1409 1410 activeInstance = (IAndroidEmulatorInstance) activeInstanceItem.getData(); 1411 1412 String activeId = 1413 ((IAndroidEmulatorInstance) activeInstanceItem.getData()) 1414 .getInstanceIdentifier(); 1415 1416 setActiveInstanceId(activeId); 1417 } 1418 else 1419 { 1420 debug("No active instance being shown at emulator view"); 1421 } 1422 1423 } 1424 1425 /** 1426 * Starts the main display associating it to the protocol. 1427 * @param handle the protocol handle 1428 * @param mainDisplay the main display object 1429 */ 1430 private void startDisplay(ProtocolHandle handle, SWTRemoteDisplay mainDisplay) 1431 { 1432 // Stop any running screens 1433 if ((mainDisplay.isActive()) && (!mainDisplay.isDisposed())) 1434 { 1435 mainDisplay.stop(); 1436 } 1437 1438 try 1439 { 1440 info("Starting main display refresh"); 1441 mainDisplay.start(handle); 1442 } 1443 catch (Exception e) 1444 { 1445 error("Viewers could not be started."); 1446 EclipseUtils.showErrorDialog(EmulatorNLS.GEN_Error, 1447 EmulatorNLS.EXC_AndroidView_ErrorStartingScreens); 1448 1449 GC gc = new GC(mainDisplay.getCanvas()); 1450 gc.fillRectangle(0, 0, mainDisplay.getScreenWidth(), mainDisplay.getScreenHeight()); 1451 gc.dispose(); 1452 } 1453 } 1454 1455 /** 1456 * Starts viewer (main display and CLI display) of the emulator instance. 1457 * @param instance the emulator instance 1458 */ 1459 protected void startVncDisplays(final IAndroidEmulatorInstance instance) 1460 { 1461 AndroidViewData viewData = instanceDataMap.get(instance); 1462 if (viewData != null) 1463 { 1464 if (viewData.getMainDisplay() != null) 1465 { 1466 startDisplay(instance.getProtocolHandle(), viewData.getMainDisplay()); 1467 } 1468 if ((viewData.getCliDisplay() != null) && instance.getHasCli()) 1469 { 1470 viewData.getCliDisplay().start(); 1471 } 1472 } 1473 } 1474 1475 /** 1476 * Stops viewer (main display and CLI display) of the emulator instance. 1477 */ 1478 private void stopVncDisplays(final IAndroidEmulatorInstance instance) 1479 { 1480 info("Stop the VNC Display " + getViewId() + " for " + instance); 1481 AndroidViewData viewData = instanceDataMap.get(instance); 1482 1483 if ((viewData != null)) 1484 { 1485 SWTRemoteDisplay mainDisplay = viewData.getMainDisplay(); 1486 if ((mainDisplay != null) && mainDisplay.isActive() && !mainDisplay.isDisposed()) 1487 { 1488 mainDisplay.stop(); 1489 if ((mainDisplay.getBackground() != null) 1490 && !mainDisplay.getBackground().isDisposed()) 1491 { 1492 mainDisplay.getBackground().dispose(); 1493 } 1494 } 1495 1496 RemoteCLIDisplay cliDisplay = viewData.getCliDisplay(); 1497 if ((cliDisplay != null) && cliDisplay.isDisplayActive() && !cliDisplay.isDisposed()) 1498 { 1499 cliDisplay.stop(); 1500 if ((cliDisplay.getBackground() != null) 1501 && !cliDisplay.getBackground().isDisposed()) 1502 { 1503 cliDisplay.getBackground().dispose(); 1504 } 1505 } 1506 } 1507 } 1508 1509 /** 1510 * @param instance 1511 */ 1512 private void stopVncProtocol(IAndroidLogicInstance instance) 1513 { 1514 AndroidEmulatorStopper.stopInstance(instance, true, false, new NullProgressMonitor()); 1515 1516 } 1517 1518 /** 1519 * Stops the execution of the vnc server if it is running on the given instance. 1520 * This acts as if the Control+C was pressed in the shell where the vnc server is executing... 1521 * @param instance 1522 */ 1523 private void stopVncServer(IAndroidEmulatorInstance instance) 1524 { 1525 StartVncServerLogic.cancelCurrentVncServerJobs(instance); 1526 } 1527 1528 /** 1529 * Class to implement the IPerspectiveListener that you be used as ParListener2 of 1530 * current page when the Emulator View is opened. It is required to code the 1531 * work-around for Sticky Views on perspectiveChanged method. 1532 */ 1533 private class PerspectiveListenerImpl implements IPerspectiveListener 1534 { 1535 1536 public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspective) 1537 { 1538 // Nothing to do. 1539 } 1540 1541 public void perspectiveChanged(IWorkbenchPage page, IPerspectiveDescriptor perspective, 1542 String changeId) 1543 { 1544 if (changeId.equals(IWorkbenchPage.CHANGE_VIEW_HIDE)) 1545 { 1546 1547 // if the emulator view was hidden 1548 if (page.findView(getViewId()) == null) 1549 { 1550 1551 // This is a "sticky view" so when it is hidden or shown for one 1552 // perspective, its state is remembered for the other ones. 1553 // However, the view reference count is just updated when 1554 // the current active perspective is changed. The code below 1555 // forces the perspective changing in order to dispose the view 1556 // immediately after it is hidden. 1557 for (IPerspectiveDescriptor pd : page.getOpenPerspectives()) 1558 { 1559 if (!pd.equals(perspective)) 1560 { 1561 page.setPerspective(pd); 1562 } 1563 } 1564 page.setPerspective(perspective); 1565 } 1566 } 1567 } 1568 } 1569 1570 /** 1571 * Class to implement IPartListener2 that you be used as ParListener2 of current page 1572 * when the Emulator View is opened. It is necessary to determine when the view is 1573 * closed. 1574 */ 1575 private class PartListenerImpl implements IPartListener2 1576 { 1577 1578 public void partActivated(IWorkbenchPartReference partRef) 1579 { 1580 // Nothing to do. 1581 } 1582 1583 public void partBroughtToTop(IWorkbenchPartReference partRef) 1584 { 1585 // Nothing to do. 1586 } 1587 1588 public void partClosed(final IWorkbenchPartReference partRef) 1589 { 1590 1591 // if view that is being closed is not THIS view. 1592 if (!partRef.getId().equals(getViewId())) 1593 { 1594 return; 1595 } 1596 1597 // executed on async mode to avoid UI blocking 1598 PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() 1599 { 1600 public void run() 1601 { 1602 boolean openedViewsExist = false; 1603 1604 for (String viewId : childrenIDs) 1605 { 1606 if (!getViewId().equals(viewId) 1607 && (partRef.getPage().findView(viewId) != null)) 1608 { 1609 openedViewsExist = true; 1610 break; 1611 } 1612 } 1613 1614 // stops all viewers and clear the tabs list 1615 Collection<IAndroidEmulatorInstance> instances = getInstancesWithViewer(); 1616 for (IAndroidEmulatorInstance instance : instances) 1617 { 1618 disposeViewer(instance); 1619 } 1620 1621 // if the tool is not being closed and there is no other emulator 1622 // view opened. 1623 if (!closingOnShutdown && !openedViewsExist) 1624 { 1625 1626 Collection<IAndroidEmulatorInstance> startedInstances = 1627 DeviceFrameworkManager.getInstance().getAllStartedInstances(); 1628 1629 boolean oneInstanceStarted = (startedInstances.size() > 0); 1630 1631 if (oneInstanceStarted 1632 && (DialogWithToggleUtils 1633 .showQuestion( 1634 STOP_BY_CLOSING_VIEW_KEY_PREFERENCE, 1635 EmulatorNLS.QUESTION_AndroidView_StopAllInstancesOnDisposeTitle, 1636 EmulatorNLS.QUESTION_AndroidView_StopAllInstancesOnDisposeMessage))) 1637 { 1638 1639 stopEmulatorInstances(); 1640 } 1641 } 1642 1643 } 1644 1645 }); 1646 } 1647 1648 public void partDeactivated(IWorkbenchPartReference partRef) 1649 { 1650 // Nothing to do. 1651 } 1652 1653 public void partHidden(IWorkbenchPartReference partRef) 1654 { 1655 // Nothing to do. 1656 } 1657 1658 public void partInputChanged(IWorkbenchPartReference partRef) 1659 { 1660 // Nothing to do. 1661 } 1662 1663 public void partOpened(IWorkbenchPartReference partRef) 1664 { 1665 // Nothing to do. 1666 } 1667 1668 public void partVisible(IWorkbenchPartReference partRef) 1669 { 1670 if (partRef.getId().equals(getViewId())) 1671 { 1672 refreshView(); 1673 } 1674 } 1675 } 1676 1677 /** 1678 * Class to implement the IWorkbenchListener that you be used as WorkbenchListener of 1679 * workbench when the Emulator View is opened. It is used to know if the view 1680 * is being closed due to workbench shutdown. 1681 */ 1682 private class WorkbenchListenerImpl implements IWorkbenchListener 1683 { 1684 1685 public void postShutdown(IWorkbench workbench) 1686 { 1687 // Nothing to do. 1688 } 1689 1690 public boolean preShutdown(IWorkbench workbench, boolean forced) 1691 { 1692 closingOnShutdown = true; 1693 1694 Collection<IAndroidEmulatorInstance> startedInstances = 1695 DeviceFrameworkManager.getInstance().getAllStartedInstances(); 1696 1697 if (startedInstances.size() > 0) 1698 { 1699 1700 boolean stopEmulatorInstances = false; 1701 if (PluginUtils.getOS() != PluginUtils.OS_LINUX) 1702 { 1703 stopEmulatorInstances = 1704 DialogWithToggleUtils.showQuestion( 1705 STOP_ALL_EMULATORS_IN_SHUTDOWN_KEY_PREFERENCE, 1706 EmulatorNLS.QUESTION_RunningInstancesOnClose_Title, 1707 EmulatorNLS.QUESTION_RunningInstancesOnClose_Text); 1708 } 1709 else 1710 { 1711 DialogWithToggleUtils.showWarning( 1712 STOP_ALL_EMULATORS_IN_SHUTDOWN_KEY_PREFERENCE, 1713 EmulatorNLS.WARN_RunningInstancesOnClose_Linux_Title, 1714 EmulatorNLS.WARN_RunningInstancesOnClose_Linux_Text); 1715 //stopEmulatorInstances = true; 1716 } 1717 1718 if (stopEmulatorInstances) 1719 { 1720 stopEmulatorInstances(); 1721 } 1722 1723 } 1724 1725 return true; 1726 } 1727 } 1728 1729 /** 1730 * Selects the tab that has this data host and set the activeHost 1731 * @param host IP address 1732 */ 1733 private void setSelection(final String host) 1734 { 1735 1736 PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() 1737 { 1738 public void run() 1739 { 1740 TabItem selectedTab = null; 1741 1742 TabItem[] tabArray = tabFolder.getItems(); 1743 1744 for (TabItem tabItem : tabArray) 1745 { 1746 String tabItemHost = 1747 ((IAndroidEmulatorInstance) tabItem.getData()).getInstanceIdentifier(); 1748 if ((host != null) && (host.equals(tabItemHost))) 1749 { 1750 selectedTab = tabItem; 1751 break; 1752 } 1753 } 1754 1755 if (selectedTab != null) 1756 { 1757 tabFolder.setSelection(selectedTab); 1758 updateMenuAndToolbars(); 1759 } 1760 1761 } 1762 }); 1763 1764 } 1765 1766 public static void updateInstanceName(final IInstance instance) 1767 { 1768 PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() 1769 { 1770 public void run() 1771 { 1772 if (!childrenIDs.isEmpty()) 1773 { 1774 AbstractAndroidView view = 1775 (AbstractAndroidView) EclipseUtils.getActiveView(childrenIDs.get(0)); 1776 if (view != null) 1777 { 1778 if ((instance != null)) 1779 { 1780 TabItem tabItem = view.getTabItem(instance); 1781 if (tabItem != null) 1782 { 1783 tabItem.setText(((IAndroidEmulatorInstance) tabItem.getData()) 1784 .getFullName()); 1785 } 1786 } 1787 } 1788 } 1789 } 1790 }); 1791 } 1792 1793 /** 1794 * Sets the skin zoom factor 1795 * 1796 * @param instance the emulator instance 1797 * @param zoom the zoom factor 1798 */ 1799 public final void setZoomFactor(IAndroidEmulatorInstance instance, double zoom) 1800 { 1801 try 1802 { 1803 AndroidViewData viewData = instanceDataMap.get(instance); 1804 if (viewData != null) 1805 { 1806 IAndroidComposite composite = viewData.getComposite(); 1807 if (composite != null) 1808 { 1809 composite.setZoomFactor(zoom); 1810 } 1811 } 1812 } 1813 catch (Exception e) 1814 { 1815 error("Detached zoom could not be set."); 1816 } 1817 } 1818 1819 /** 1820 * Gets the skin zoom factor 1821 * 1822 * @param instance the emulator instance 1823 * @return the zoom factor 1824 */ 1825 public final double getZoomFactor(IAndroidEmulatorInstance instance) 1826 { 1827 double zoomFactor = 0.0; 1828 AndroidViewData viewData = instanceDataMap.get(instance); 1829 if (viewData != null) 1830 { 1831 IAndroidComposite composite = viewData.getComposite(); 1832 if (composite != null) 1833 { 1834 zoomFactor = composite.getZoomFactor(); 1835 } 1836 } 1837 return zoomFactor; 1838 } 1839 }