/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.sdkuilib.internal.repository.ui;
import com.android.SdkConstants;
import com.android.sdklib.devices.DeviceManager;
import com.android.sdklib.internal.avd.AvdInfo;
import com.android.sdklib.internal.repository.ITaskFactory;
import com.android.sdkuilib.internal.repository.AboutDialog;
import com.android.sdkuilib.internal.repository.MenuBarWrapper;
import com.android.sdkuilib.internal.repository.SettingsController;
import com.android.sdkuilib.internal.repository.SettingsDialog;
import com.android.sdkuilib.internal.repository.UpdaterData;
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
import com.android.sdkuilib.internal.repository.ui.DeviceManagerPage.IAvdCreatedListener;
import com.android.sdkuilib.repository.AvdManagerWindow.AvdInvocationContext;
import com.android.sdkuilib.repository.ISdkChangeListener;
import com.android.sdkuilib.repository.SdkUpdaterWindow;
import com.android.sdkuilib.ui.GridDataBuilder;
import com.android.sdkuilib.ui.GridLayoutBuilder;
import com.android.utils.ILogger;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
/**
* This is an intermediate version of the {@link AvdManagerPage}
* wrapped in its own standalone window for use from the SDK Manager 2.
*/
public class AvdManagerWindowImpl1 {
private static final String APP_NAME = "Android Virtual Device Manager";
private static final String APP_NAME_MAC_MENU = "AVD Manager";
private static final String SIZE_POS_PREFIX = "avdman1"; //$NON-NLS-1$
private final Shell mParentShell;
private final AvdInvocationContext mContext;
/** Internal data shared between the window and its pages. */
private final UpdaterData mUpdaterData;
/** True if this window created the UpdaterData, in which case it needs to dispose it. */
private final boolean mOwnUpdaterData;
private final DeviceManager mDeviceManager;
// --- UI members ---
protected Shell mShell;
private AvdManagerPage mAvdPage;
private SettingsController mSettingsController;
private TabFolder mTabFolder;
/**
* Creates a new window. Caller must call open(), which will block.
*
* @param parentShell Parent shell.
* @param sdkLog Logger. Cannot be null.
* @param osSdkRoot The OS path to the SDK root.
* @param context The {@link AvdInvocationContext} to change the behavior depending on who's
* opening the SDK Manager.
*/
public AvdManagerWindowImpl1(
Shell parentShell,
ILogger sdkLog,
String osSdkRoot,
AvdInvocationContext context) {
mParentShell = parentShell;
mContext = context;
mUpdaterData = new UpdaterData(osSdkRoot, sdkLog);
mOwnUpdaterData = true;
mDeviceManager = DeviceManager.createInstance(osSdkRoot, sdkLog);
}
/**
* Creates a new window. Caller must call open(), which will block.
* <p/>
* This is to be used when the window is opened from {@link SdkUpdaterWindowImpl2}
* to share the same {@link UpdaterData} structure.
*
* @param parentShell Parent shell.
* @param updaterData The parent's updater data.
* @param context The {@link AvdInvocationContext} to change the behavior depending on who's
* opening the SDK Manager.
*/
public AvdManagerWindowImpl1(
Shell parentShell,
UpdaterData updaterData,
AvdInvocationContext context) {
mParentShell = parentShell;
mContext = context;
mUpdaterData = updaterData;
mOwnUpdaterData = false;
mDeviceManager = DeviceManager.createInstance(mUpdaterData.getOsSdkRoot(),
mUpdaterData.getSdkLog());
}
/**
* Opens the window.
* @wbp.parser.entryPoint
*/
public void open() {
if (mParentShell == null) {
Display.setAppName(APP_NAME); //$hide$ (hide from SWT designer)
}
createShell();
preCreateContent();
createContents();
createMenuBar();
mShell.open();
mShell.layout();
boolean ok = postCreateContent();
if (ok && mContext == AvdInvocationContext.STANDALONE) {
Display display = Display.getDefault();
while (!mShell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
dispose(); //$hide$
}
}
private void createShell() {
// The AVD Manager must use a shell trim when standalone
// or a dialog trim when invoked from somewhere else.
int style = SWT.SHELL_TRIM;
if (mContext != AvdInvocationContext.STANDALONE) {
style |= SWT.APPLICATION_MODAL;
}
mShell = new Shell(mParentShell, style);
mShell.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
ShellSizeAndPos.saveSizeAndPos(mShell, SIZE_POS_PREFIX); //$hide$
onAndroidSdkUpdaterDispose(); //$hide$
mAvdPage.dispose(); //$hide$
}
});
GridLayout glShell = new GridLayout(2, false);
mShell.setLayout(glShell);
mShell.setMinimumSize(new Point(500, 300));
mShell.setSize(700, 500);
mShell.setText(APP_NAME);
ShellSizeAndPos.loadSizeAndPos(mShell, SIZE_POS_PREFIX);
}
private void createContents() {
mTabFolder = new TabFolder(mShell, SWT.NONE);
GridDataBuilder.create(mTabFolder).fill().grab().hSpan(2);
// avd tab
TabItem avdTabItem = new TabItem(mTabFolder, SWT.NONE);
avdTabItem.setText("Android Virtual Devices");
createAvdTab(mTabFolder, avdTabItem);
// device tab
TabItem devTabItem = new TabItem(mTabFolder, SWT.NONE);
devTabItem.setText("Device Definitions");
createDeviceTab(mTabFolder, devTabItem);
}
private void createAvdTab(TabFolder tabFolder, TabItem avdTabItem) {
Composite root = new Composite(tabFolder, SWT.NONE);
avdTabItem.setControl(root);
GridLayoutBuilder.create(root).columns(1);
mAvdPage = new AvdManagerPage(root, SWT.NONE, mUpdaterData, mDeviceManager);
GridDataBuilder.create(mAvdPage).fill().grab();
}
private void createDeviceTab(TabFolder tabFolder, TabItem devTabItem) {
Composite root = new Composite(tabFolder, SWT.NONE);
devTabItem.setControl(root);
GridLayoutBuilder.create(root).columns(1);
DeviceManagerPage devicePage =
new DeviceManagerPage(root, SWT.NONE, mUpdaterData, mDeviceManager);
GridDataBuilder.create(devicePage).fill().grab();
devicePage.setAvdCreatedListener(new IAvdCreatedListener() {
@Override
public void onAvdCreated(AvdInfo avdInfo) {
if (avdInfo != null) {
mTabFolder.setSelection(0); // display mAvdPage
mAvdPage.selectAvd(avdInfo, true /*reloadAvdList*/);
}
}
});
}
@SuppressWarnings("unused")
// MenuBarWrapper works using side effects
private void createMenuBar() {
Menu menuBar = new Menu(mShell, SWT.BAR);
mShell.setMenuBar(menuBar);
// Only create the tools menu when running as standalone.
// We don't need the tools menu when invoked from the IDE, or the SDK Manager
// or from the AVD Chooser dialog. The only point of the tools menu is to
// get the about box, and invoke Tools > SDK Manager, which we don't
// need to do in these cases.
if (mContext == AvdInvocationContext.STANDALONE) {
MenuItem menuBarTools = new MenuItem(menuBar, SWT.CASCADE);
menuBarTools.setText("Tools");
Menu menuTools = new Menu(menuBarTools);
menuBarTools.setMenu(menuTools);
MenuItem manageSdk = new MenuItem(menuTools, SWT.NONE);
manageSdk.setText("Manage SDK...");
manageSdk.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
onSdkManager();
}
});
try {
new MenuBarWrapper(APP_NAME_MAC_MENU, menuTools) {
@Override
public void onPreferencesMenuSelected() {
SettingsDialog sd = new SettingsDialog(mShell, mUpdaterData);
sd.open();
}
@Override
public void onAboutMenuSelected() {
AboutDialog ad = new AboutDialog(mShell, mUpdaterData);
ad.open();
}
@Override
public void printError(String format, Object... args) {
if (mUpdaterData != null) {
mUpdaterData.getSdkLog().error(null, format, args);
}
}
};
} catch (Throwable e) {
mUpdaterData.getSdkLog().error(e, "Failed to setup menu bar");
e.printStackTrace();
}
}
}
// -- Start of internal part ----------
// Hide everything down-below from SWT designer
//$hide>>$
// --- Public API -----------
/**
* Adds a new listener to be notified when a change is made to the content of the SDK.
*/
public void addListener(ISdkChangeListener listener) {
mUpdaterData.addListeners(listener);
}
/**
* Removes a new listener to be notified anymore when a change is made to the content of
* the SDK.
*/
public void removeListener(ISdkChangeListener listener) {
mUpdaterData.removeListener(listener);
}
// --- Internals & UI Callbacks -----------
/**
* Called before the UI is created.
*/
private void preCreateContent() {
mUpdaterData.setWindowShell(mShell);
// We need the UI factory to create the UI
mUpdaterData.setImageFactory(new ImageFactory(mShell.getDisplay()));
// Note: we can't create the TaskFactory yet because we need the UI
// to be created first, so this is done in postCreateContent().
}
/**
* Once the UI has been created, initializes the content.
* This creates the pages, selects the first one, setup sources and scan for local folders.
*
* Returns true if we should show the window.
*/
private boolean postCreateContent() {
setWindowImage(mShell);
setupSources();
initializeSettings();
if (mUpdaterData.checkIfInitFailed()) {
return false;
}
mUpdaterData.broadcastOnSdkLoaded();
return true;
}
/**
* Creates the icon of the window shell.
*
* @param shell The shell on which to put the icon
*/
private void setWindowImage(Shell shell) {
String imageName = "android_icon_16.png"; //$NON-NLS-1$
if (SdkConstants.currentPlatform() == SdkConstants.PLATFORM_DARWIN) {
imageName = "android_icon_128.png";
}
if (mUpdaterData != null) {
ImageFactory imgFactory = mUpdaterData.getImageFactory();
if (imgFactory != null) {
shell.setImage(imgFactory.getImageByName(imageName));
}
}
}
/**
* Called by the main loop when the window has been disposed.
*/
private void dispose() {
mUpdaterData.getSources().saveUserAddons(mUpdaterData.getSdkLog());
}
/**
* Callback called when the window shell is disposed.
*/
private void onAndroidSdkUpdaterDispose() {
if (mOwnUpdaterData && mUpdaterData != null) {
ImageFactory imgFactory = mUpdaterData.getImageFactory();
if (imgFactory != null) {
imgFactory.dispose();
}
}
}
/**
* Used to initialize the sources.
*/
private void setupSources() {
mUpdaterData.setupDefaultSources();
}
/**
* Initializes settings.
* This must be called after addExtraPages(), which created a settings page.
* Iterate through all the pages to find the first (and supposedly unique) setting page,
* and use it to load and apply these settings.
*/
private void initializeSettings() {
mSettingsController = mUpdaterData.getSettingsController();
mSettingsController.loadSettings();
mSettingsController.applySettings();
}
private void onSdkManager() {
ITaskFactory oldFactory = mUpdaterData.getTaskFactory();
try {
SdkUpdaterWindowImpl2 win = new SdkUpdaterWindowImpl2(
mShell,
mUpdaterData,
SdkUpdaterWindow.SdkInvocationContext.AVD_MANAGER);
win.open();
} catch (Exception e) {
mUpdaterData.getSdkLog().error(e, "SDK Manager window error");
} finally {
mUpdaterData.setTaskFactory(oldFactory);
}
}
}