1 /* 2 * Copyright (C) 2011 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 17 package com.android.sdkuilib.internal.repository.sdkman2; 18 19 20 import com.android.sdklib.ISdkLog; 21 import com.android.sdklib.SdkConstants; 22 import com.android.sdklib.internal.repository.ITaskFactory; 23 import com.android.sdkuilib.internal.repository.AboutDialog; 24 import com.android.sdkuilib.internal.repository.MenuBarWrapper; 25 import com.android.sdkuilib.internal.repository.SettingsController; 26 import com.android.sdkuilib.internal.repository.SettingsDialog; 27 import com.android.sdkuilib.internal.repository.UpdaterData; 28 import com.android.sdkuilib.internal.repository.icons.ImageFactory; 29 import com.android.sdkuilib.internal.repository.sdkman1.AvdManagerPage; 30 import com.android.sdkuilib.repository.AvdManagerWindow.AvdInvocationContext; 31 import com.android.sdkuilib.repository.ISdkChangeListener; 32 import com.android.sdkuilib.repository.SdkUpdaterWindow; 33 34 import org.eclipse.swt.SWT; 35 import org.eclipse.swt.events.DisposeEvent; 36 import org.eclipse.swt.events.DisposeListener; 37 import org.eclipse.swt.events.SelectionAdapter; 38 import org.eclipse.swt.events.SelectionEvent; 39 import org.eclipse.swt.graphics.Point; 40 import org.eclipse.swt.layout.GridData; 41 import org.eclipse.swt.layout.GridLayout; 42 import org.eclipse.swt.widgets.Display; 43 import org.eclipse.swt.widgets.Menu; 44 import org.eclipse.swt.widgets.MenuItem; 45 import org.eclipse.swt.widgets.Shell; 46 47 /** 48 * This is an intermediate version of the {@link AvdManagerPage} 49 * wrapped in its own standalone window for use from the SDK Manager 2. 50 */ 51 public class AvdManagerWindowImpl1 { 52 53 private static final String APP_NAME = "Android Virtual Device Manager"; 54 private static final String APP_NAME_MAC_MENU = "AVD Manager"; 55 private static final String SIZE_POS_PREFIX = "avdman1"; //$NON-NLS-1$ 56 57 private final Shell mParentShell; 58 private final AvdInvocationContext mContext; 59 /** Internal data shared between the window and its pages. */ 60 private final UpdaterData mUpdaterData; 61 /** True if this window created the UpdaterData, in which case it needs to dispose it. */ 62 private final boolean mOwnUpdaterData; 63 64 // --- UI members --- 65 66 protected Shell mShell; 67 private AvdManagerPage mAvdPage; 68 private SettingsController mSettingsController; 69 70 /** 71 * Creates a new window. Caller must call open(), which will block. 72 * 73 * @param parentShell Parent shell. 74 * @param sdkLog Logger. Cannot be null. 75 * @param osSdkRoot The OS path to the SDK root. 76 * @param context The {@link AvdInvocationContext} to change the behavior depending on who's 77 * opening the SDK Manager. 78 */ 79 public AvdManagerWindowImpl1( 80 Shell parentShell, 81 ISdkLog sdkLog, 82 String osSdkRoot, 83 AvdInvocationContext context) { 84 mParentShell = parentShell; 85 mContext = context; 86 mUpdaterData = new UpdaterData(osSdkRoot, sdkLog); 87 mOwnUpdaterData = true; 88 } 89 90 /** 91 * Creates a new window. Caller must call open(), which will block. 92 * <p/> 93 * This is to be used when the window is opened from {@link SdkUpdaterWindowImpl2} 94 * to share the same {@link UpdaterData} structure. 95 * 96 * @param parentShell Parent shell. 97 * @param updaterData The parent's updater data. 98 * @param context The {@link AvdInvocationContext} to change the behavior depending on who's 99 * opening the SDK Manager. 100 */ 101 public AvdManagerWindowImpl1( 102 Shell parentShell, 103 UpdaterData updaterData, 104 AvdInvocationContext context) { 105 mParentShell = parentShell; 106 mContext = context; 107 mUpdaterData = updaterData; 108 mOwnUpdaterData = false; 109 } 110 111 /** 112 * Opens the window. 113 * @wbp.parser.entryPoint 114 */ 115 public void open() { 116 if (mParentShell == null) { 117 Display.setAppName(APP_NAME); //$hide$ (hide from SWT designer) 118 } 119 120 createShell(); 121 preCreateContent(); 122 createContents(); 123 createMenuBar(); 124 mShell.open(); 125 mShell.layout(); 126 127 boolean ok = postCreateContent(); 128 129 if (ok && mContext == AvdInvocationContext.STANDALONE) { 130 Display display = Display.getDefault(); 131 while (!mShell.isDisposed()) { 132 if (!display.readAndDispatch()) { 133 display.sleep(); 134 } 135 } 136 137 dispose(); //$hide$ 138 } 139 } 140 141 private void createShell() { 142 // The AVD Manager must use a shell trim when standalone 143 // or a dialog trim when invoked from somewhere else. 144 int style = SWT.SHELL_TRIM; 145 if (mContext != AvdInvocationContext.STANDALONE) { 146 style |= SWT.APPLICATION_MODAL; 147 } 148 149 mShell = new Shell(mParentShell, style); 150 mShell.addDisposeListener(new DisposeListener() { 151 @Override 152 public void widgetDisposed(DisposeEvent e) { 153 ShellSizeAndPos.saveSizeAndPos(mShell, SIZE_POS_PREFIX); //$hide$ 154 onAndroidSdkUpdaterDispose(); //$hide$ 155 } 156 }); 157 158 GridLayout glShell = new GridLayout(2, false); 159 glShell.verticalSpacing = 0; 160 glShell.horizontalSpacing = 0; 161 glShell.marginWidth = 0; 162 glShell.marginHeight = 0; 163 mShell.setLayout(glShell); 164 165 mShell.setMinimumSize(new Point(500, 300)); 166 mShell.setSize(700, 500); 167 mShell.setText(APP_NAME); 168 169 ShellSizeAndPos.loadSizeAndPos(mShell, SIZE_POS_PREFIX); 170 } 171 172 private void createContents() { 173 174 mAvdPage = new AvdManagerPage(mShell, SWT.NONE, mUpdaterData); 175 mAvdPage.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); 176 } 177 178 @SuppressWarnings("unused") // MenuBarWrapper works using side effects 179 private void createMenuBar() { 180 181 if (mContext != AvdInvocationContext.STANDALONE) { 182 return; 183 } 184 185 Menu menuBar = new Menu(mShell, SWT.BAR); 186 mShell.setMenuBar(menuBar); 187 188 MenuItem menuBarTools = new MenuItem(menuBar, SWT.CASCADE); 189 menuBarTools.setText("Tools"); 190 191 Menu menuTools = new Menu(menuBarTools); 192 menuBarTools.setMenu(menuTools); 193 194 MenuItem manageSdk = new MenuItem(menuTools, SWT.NONE); 195 manageSdk.setText("Manage SDK..."); 196 manageSdk.addSelectionListener(new SelectionAdapter() { 197 @Override 198 public void widgetSelected(SelectionEvent event) { 199 onSdkManager(); 200 } 201 }); 202 203 if (mContext != AvdInvocationContext.IDE) { 204 // Note: when invoked from an IDE, the SwtMenuBar library isn't 205 // available. This means this source should not directly import 206 // any of SwtMenuBar classes, otherwise the whole window class 207 // would fail to load. The MenuBarWrapper below helps to make 208 // that indirection. 209 210 try { 211 new MenuBarWrapper(APP_NAME_MAC_MENU, menuTools) { 212 @Override 213 public void onPreferencesMenuSelected() { 214 SettingsDialog sd = new SettingsDialog(mShell, mUpdaterData); 215 sd.open(); 216 } 217 218 @Override 219 public void onAboutMenuSelected() { 220 AboutDialog ad = new AboutDialog(mShell, mUpdaterData); 221 ad.open(); 222 } 223 224 @Override 225 public void printError(String format, Object... args) { 226 if (mUpdaterData != null) { 227 mUpdaterData.getSdkLog().error(null, format, args); 228 } 229 } 230 }; 231 } catch (Exception e) { 232 mUpdaterData.getSdkLog().error(e, "Failed to setup menu bar"); 233 e.printStackTrace(); 234 } 235 } 236 } 237 238 239 // -- Start of internal part ---------- 240 // Hide everything down-below from SWT designer 241 //$hide>>$ 242 243 // --- Public API ----------- 244 245 /** 246 * Adds a new listener to be notified when a change is made to the content of the SDK. 247 */ 248 public void addListener(ISdkChangeListener listener) { 249 mUpdaterData.addListeners(listener); 250 } 251 252 /** 253 * Removes a new listener to be notified anymore when a change is made to the content of 254 * the SDK. 255 */ 256 public void removeListener(ISdkChangeListener listener) { 257 mUpdaterData.removeListener(listener); 258 } 259 260 // --- Internals & UI Callbacks ----------- 261 262 /** 263 * Called before the UI is created. 264 */ 265 private void preCreateContent() { 266 mUpdaterData.setWindowShell(mShell); 267 // We need the UI factory to create the UI 268 mUpdaterData.setImageFactory(new ImageFactory(mShell.getDisplay())); 269 // Note: we can't create the TaskFactory yet because we need the UI 270 // to be created first, so this is done in postCreateContent(). 271 } 272 273 /** 274 * Once the UI has been created, initializes the content. 275 * This creates the pages, selects the first one, setup sources and scan for local folders. 276 * 277 * Returns true if we should show the window. 278 */ 279 private boolean postCreateContent() { 280 setWindowImage(mShell); 281 282 setupSources(); 283 initializeSettings(); 284 285 if (mUpdaterData.checkIfInitFailed()) { 286 return false; 287 } 288 289 mUpdaterData.broadcastOnSdkLoaded(); 290 291 return true; 292 } 293 294 /** 295 * Creates the icon of the window shell. 296 * 297 * @param shell The shell on which to put the icon 298 */ 299 private void setWindowImage(Shell shell) { 300 String imageName = "android_icon_16.png"; //$NON-NLS-1$ 301 if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_DARWIN) { 302 imageName = "android_icon_128.png"; 303 } 304 305 if (mUpdaterData != null) { 306 ImageFactory imgFactory = mUpdaterData.getImageFactory(); 307 if (imgFactory != null) { 308 shell.setImage(imgFactory.getImageByName(imageName)); 309 } 310 } 311 } 312 313 /** 314 * Called by the main loop when the window has been disposed. 315 */ 316 private void dispose() { 317 mUpdaterData.getSources().saveUserAddons(mUpdaterData.getSdkLog()); 318 } 319 320 /** 321 * Callback called when the window shell is disposed. 322 */ 323 private void onAndroidSdkUpdaterDispose() { 324 if (mOwnUpdaterData && mUpdaterData != null) { 325 ImageFactory imgFactory = mUpdaterData.getImageFactory(); 326 if (imgFactory != null) { 327 imgFactory.dispose(); 328 } 329 } 330 } 331 332 /** 333 * Used to initialize the sources. 334 */ 335 private void setupSources() { 336 mUpdaterData.setupDefaultSources(); 337 } 338 339 /** 340 * Initializes settings. 341 * This must be called after addExtraPages(), which created a settings page. 342 * Iterate through all the pages to find the first (and supposedly unique) setting page, 343 * and use it to load and apply these settings. 344 */ 345 private void initializeSettings() { 346 mSettingsController = mUpdaterData.getSettingsController(); 347 mSettingsController.loadSettings(); 348 mSettingsController.applySettings(); 349 } 350 351 private void onSdkManager() { 352 ITaskFactory oldFactory = mUpdaterData.getTaskFactory(); 353 354 try { 355 SdkUpdaterWindowImpl2 win = new SdkUpdaterWindowImpl2( 356 mShell, 357 mUpdaterData, 358 SdkUpdaterWindow.SdkInvocationContext.AVD_MANAGER); 359 360 win.open(); 361 } catch (Exception e) { 362 mUpdaterData.getSdkLog().error(e, "SDK Manager window error"); 363 } finally { 364 mUpdaterData.setTaskFactory(oldFactory); 365 } 366 } 367 } 368