am d13d440d: Emulator launch options dialog.
Merge commit 'd13d440d43a744ab6e596e52cf53eef79d222d01' * commit 'd13d440d43a744ab6e596e52cf53eef79d222d01': Emulator launch options dialog.
This commit is contained in:
@@ -110,7 +110,7 @@ public final class AvdManager {
|
|||||||
/**
|
/**
|
||||||
* Pattern to match pixel-sized skin "names", e.g. "320x480".
|
* Pattern to match pixel-sized skin "names", e.g. "320x480".
|
||||||
*/
|
*/
|
||||||
public final static Pattern NUMERIC_SKIN_SIZE = Pattern.compile("[0-9]{2,}x[0-9]{2,}"); //$NON-NLS-1$
|
public final static Pattern NUMERIC_SKIN_SIZE = Pattern.compile("([0-9]{2,})x([0-9]{2,})"); //$NON-NLS-1$
|
||||||
|
|
||||||
private final static String USERDATA_IMG = "userdata.img"; //$NON-NLS-1$
|
private final static String USERDATA_IMG = "userdata.img"; //$NON-NLS-1$
|
||||||
private final static String CONFIG_INI = "config.ini"; //$NON-NLS-1$
|
private final static String CONFIG_INI = "config.ini"; //$NON-NLS-1$
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ public class AvdManagerPage extends Composite implements ISdkListener {
|
|||||||
mUpdaterData.getOsSdkRoot(),
|
mUpdaterData.getOsSdkRoot(),
|
||||||
mUpdaterData.getAvdManager(),
|
mUpdaterData.getAvdManager(),
|
||||||
DisplayMode.MANAGER);
|
DisplayMode.MANAGER);
|
||||||
|
mAvdSelector.setSettingsController(mUpdaterData.getSettingsController());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -52,6 +52,12 @@ public interface ISettingsPage {
|
|||||||
* Default: True.
|
* Default: True.
|
||||||
*/
|
*/
|
||||||
public static final String KEY_ASK_ADB_RESTART = "sdkman.ask.adb.restart"; //$NON-NLS-1$
|
public static final String KEY_ASK_ADB_RESTART = "sdkman.ask.adb.restart"; //$NON-NLS-1$
|
||||||
|
/**
|
||||||
|
* Setting to set the density of the monitor.
|
||||||
|
* Type: Integer.
|
||||||
|
* Default: -1
|
||||||
|
*/
|
||||||
|
public static final String KEY_MONITOR_DENSITY = "sdkman.monitor.density"; //$NON-NLS-1$
|
||||||
|
|
||||||
/** Loads settings from the given {@link Properties} container and update the page UI. */
|
/** Loads settings from the given {@link Properties} container and update the page UI. */
|
||||||
public abstract void loadSettings(Properties in_settings);
|
public abstract void loadSettings(Properties in_settings);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public class SettingsController {
|
|||||||
//--- Access to settings ------------
|
//--- Access to settings ------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of the ISettingsPage#KEY_FORCE_HTTP setting.
|
* Returns the value of the {@link ISettingsPage#KEY_FORCE_HTTP} setting.
|
||||||
* @see ISettingsPage#KEY_FORCE_HTTP
|
* @see ISettingsPage#KEY_FORCE_HTTP
|
||||||
*/
|
*/
|
||||||
public boolean getForceHttp() {
|
public boolean getForceHttp() {
|
||||||
@@ -54,7 +54,7 @@ public class SettingsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of the ISettingsPage#KEY_ASK_ADB_RESTART setting.
|
* Returns the value of the {@link ISettingsPage#KEY_ASK_ADB_RESTART} setting.
|
||||||
* @see ISettingsPage#KEY_ASK_ADB_RESTART
|
* @see ISettingsPage#KEY_ASK_ADB_RESTART
|
||||||
*/
|
*/
|
||||||
public boolean getAskBeforeAdbRestart() {
|
public boolean getAskBeforeAdbRestart() {
|
||||||
@@ -66,7 +66,7 @@ public class SettingsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of the ISettingsPage#KEY_SHOW_UPDATE_ONLY setting.
|
* Returns the value of the {@link ISettingsPage#KEY_SHOW_UPDATE_ONLY} setting.
|
||||||
* @see ISettingsPage#KEY_SHOW_UPDATE_ONLY
|
* @see ISettingsPage#KEY_SHOW_UPDATE_ONLY
|
||||||
*/
|
*/
|
||||||
public boolean getShowUpdateOnly() {
|
public boolean getShowUpdateOnly() {
|
||||||
@@ -78,7 +78,7 @@ public class SettingsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the value of the ISettingsPage#KEY_SHOW_UPDATE_ONLY setting.
|
* Sets the value of the {@link ISettingsPage#KEY_SHOW_UPDATE_ONLY} setting.
|
||||||
* @param enabled True if only compatible update items should be shown.
|
* @param enabled True if only compatible update items should be shown.
|
||||||
* @see ISettingsPage#KEY_SHOW_UPDATE_ONLY
|
* @see ISettingsPage#KEY_SHOW_UPDATE_ONLY
|
||||||
*/
|
*/
|
||||||
@@ -86,6 +86,32 @@ public class SettingsController {
|
|||||||
setSetting(ISettingsPage.KEY_SHOW_UPDATE_ONLY, enabled);
|
setSetting(ISettingsPage.KEY_SHOW_UPDATE_ONLY, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the {@link ISettingsPage#KEY_MONITOR_DENSITY} setting
|
||||||
|
* @see ISettingsPage#KEY_MONITOR_DENSITY
|
||||||
|
*/
|
||||||
|
public int getMonitorDensity() {
|
||||||
|
String value = mProperties.getProperty(ISettingsPage.KEY_MONITOR_DENSITY, null);
|
||||||
|
if (value == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(value);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of the {@link ISettingsPage#KEY_MONITOR_DENSITY} setting.
|
||||||
|
* @param density the density of the monitor
|
||||||
|
* @see ISettingsPage#KEY_MONITOR_DENSITY
|
||||||
|
*/
|
||||||
|
public void setMonitorDensity(int density) {
|
||||||
|
mProperties.setProperty(ISettingsPage.KEY_MONITOR_DENSITY, Integer.toString(density));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal helper to set a boolean setting.
|
* Internal helper to set a boolean setting.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
|
|||||||
import com.android.sdklib.internal.avd.AvdManager.AvdInfo.AvdStatus;
|
import com.android.sdklib.internal.avd.AvdManager.AvdInfo.AvdStatus;
|
||||||
import com.android.sdklib.internal.repository.ITask;
|
import com.android.sdklib.internal.repository.ITask;
|
||||||
import com.android.sdklib.internal.repository.ITaskMonitor;
|
import com.android.sdklib.internal.repository.ITaskMonitor;
|
||||||
|
import com.android.sdkuilib.internal.repository.SettingsController;
|
||||||
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
|
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
|
||||||
import com.android.sdkuilib.internal.tasks.ProgressTask;
|
import com.android.sdkuilib.internal.tasks.ProgressTask;
|
||||||
import com.android.sdkuilib.repository.UpdaterWindow;
|
import com.android.sdkuilib.repository.UpdaterWindow;
|
||||||
@@ -95,6 +96,8 @@ public final class AvdSelector {
|
|||||||
private Image mOkImage;
|
private Image mOkImage;
|
||||||
private Image mBrokenImage;
|
private Image mBrokenImage;
|
||||||
|
|
||||||
|
private SettingsController mController;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The display mode of the AVD Selector.
|
* The display mode of the AVD Selector.
|
||||||
@@ -380,6 +383,14 @@ public final class AvdSelector {
|
|||||||
DisplayMode displayMode) {
|
DisplayMode displayMode) {
|
||||||
this(parent, osSdkPath, manager, new TargetBasedFilter(filter), displayMode);
|
this(parent, osSdkPath, manager, new TargetBasedFilter(filter), displayMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an optional SettingsController.
|
||||||
|
* @param controller the controller.
|
||||||
|
*/
|
||||||
|
public void setSettingsController(SettingsController controller) {
|
||||||
|
mController = controller;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Sets the table grid layout data.
|
* Sets the table grid layout data.
|
||||||
*
|
*
|
||||||
@@ -898,52 +909,70 @@ public final class AvdSelector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String path = mOsSdkPath +
|
AvdStartDialog dialog = new AvdStartDialog(mTable.getShell(), avdInfo, mOsSdkPath,
|
||||||
File.separator +
|
mController);
|
||||||
SdkConstants.OS_SDK_TOOLS_FOLDER +
|
if (dialog.open() == Window.OK) {
|
||||||
SdkConstants.FN_EMULATOR;
|
String path = mOsSdkPath +
|
||||||
|
File.separator +
|
||||||
|
SdkConstants.OS_SDK_TOOLS_FOLDER +
|
||||||
|
SdkConstants.FN_EMULATOR;
|
||||||
|
|
||||||
final String avdName = avdInfo.getName();
|
final String avdName = avdInfo.getName();
|
||||||
|
|
||||||
// build the command line based on the available parameters.
|
// build the command line based on the available parameters.
|
||||||
ArrayList<String> list = new ArrayList<String>();
|
ArrayList<String> list = new ArrayList<String>();
|
||||||
list.add(path);
|
list.add(path);
|
||||||
list.add("-avd"); //$NON-NLS-1$
|
list.add("-avd"); //$NON-NLS-1$
|
||||||
list.add(avdName);
|
list.add(avdName);
|
||||||
|
if (dialog.getWipeData()) {
|
||||||
|
list.add("-wipe-data"); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
float scale = dialog.getScale();
|
||||||
|
if (scale != 0.f) {
|
||||||
|
// do the rounding ourselves. This is because %.1f will write .4899 as .4
|
||||||
|
scale = Math.round(scale * 100);
|
||||||
|
scale /= 100.f;
|
||||||
|
list.add("-scale"); //$NON-NLS-1$
|
||||||
|
list.add(String.format("%.2f", scale)); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
// convert the list into an array for the call to exec.
|
// convert the list into an array for the call to exec.
|
||||||
final String[] command = list.toArray(new String[list.size()]);
|
final String[] command = list.toArray(new String[list.size()]);
|
||||||
|
|
||||||
// launch the emulator
|
// launch the emulator
|
||||||
new ProgressTask(mTable.getShell(),
|
new ProgressTask(mTable.getShell(),
|
||||||
"Starting Android Emulator",
|
"Starting Android Emulator",
|
||||||
new ITask() {
|
new ITask() {
|
||||||
public void run(ITaskMonitor monitor) {
|
public void run(ITaskMonitor monitor) {
|
||||||
try {
|
try {
|
||||||
monitor.setDescription("Starting emulator for AVD '%1$s'", avdName);
|
monitor.setDescription("Starting emulator for AVD '%1$s'",
|
||||||
int n = 10;
|
avdName);
|
||||||
monitor.setProgressMax(n);
|
int n = 10;
|
||||||
Process process = Runtime.getRuntime().exec(command);
|
monitor.setProgressMax(n);
|
||||||
grabEmulatorOutput(process, monitor);
|
Process process = Runtime.getRuntime().exec(command);
|
||||||
|
grabEmulatorOutput(process, monitor);
|
||||||
|
|
||||||
// This small wait prevents the dialog from closing too fast:
|
// This small wait prevents the dialog from closing too fast:
|
||||||
// When it works, the emulator returns immediately, even if no UI
|
// When it works, the emulator returns immediately, even if
|
||||||
// is shown yet. And when it fails (because the AVD is locked/running)
|
// no UI is shown yet. And when it fails (because the AVD is
|
||||||
// if we don't have a wait we don't capture the error for some reason.
|
// locked/running)
|
||||||
for (int i = 0; i < n; i++) {
|
// if we don't have a wait we don't capture the error for
|
||||||
try {
|
// some reason.
|
||||||
Thread.sleep(100);
|
for (int i = 0; i < n; i++) {
|
||||||
monitor.incProgress(1);
|
try {
|
||||||
} catch (InterruptedException e) {
|
Thread.sleep(100);
|
||||||
// ignore
|
monitor.incProgress(1);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
monitor.setResult("Failed to start emulator: %1$s",
|
||||||
|
e.getMessage());
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
|
||||||
monitor.setResult("Failed to start emulator: %1$s", e.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,480 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009 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.widgets;
|
||||||
|
|
||||||
|
import com.android.sdklib.internal.avd.AvdManager;
|
||||||
|
import com.android.sdklib.internal.avd.AvdManager.AvdInfo;
|
||||||
|
import com.android.sdkuilib.internal.repository.SettingsController;
|
||||||
|
|
||||||
|
import org.eclipse.jface.dialogs.Dialog;
|
||||||
|
import org.eclipse.jface.dialogs.IDialogConstants;
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.events.ModifyEvent;
|
||||||
|
import org.eclipse.swt.events.ModifyListener;
|
||||||
|
import org.eclipse.swt.events.SelectionAdapter;
|
||||||
|
import org.eclipse.swt.events.SelectionEvent;
|
||||||
|
import org.eclipse.swt.events.VerifyEvent;
|
||||||
|
import org.eclipse.swt.events.VerifyListener;
|
||||||
|
import org.eclipse.swt.layout.GridData;
|
||||||
|
import org.eclipse.swt.layout.GridLayout;
|
||||||
|
import org.eclipse.swt.widgets.Button;
|
||||||
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
import org.eclipse.swt.widgets.Control;
|
||||||
|
import org.eclipse.swt.widgets.Group;
|
||||||
|
import org.eclipse.swt.widgets.Label;
|
||||||
|
import org.eclipse.swt.widgets.Shell;
|
||||||
|
import org.eclipse.swt.widgets.Text;
|
||||||
|
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog dealing with emulator launch options. The following options are supported:
|
||||||
|
* <ul>
|
||||||
|
* <li>-wipe-data</li>
|
||||||
|
* <li>-scale</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* Values are stored (in the class as static field) to be reused while the app is still running.
|
||||||
|
* The Monitor dpi is stored in the settings if availabe.
|
||||||
|
*/
|
||||||
|
final class AvdStartDialog extends Dialog {
|
||||||
|
// static field to reuse values during the same session.
|
||||||
|
private static boolean sWipeData = false;
|
||||||
|
private static int sMonitorDpi = 72; // used if there's no setting controller.
|
||||||
|
private static final Map<String, String> sSkinScaling = new HashMap<String, String>();
|
||||||
|
|
||||||
|
private static final Pattern sScreenSizePattern = Pattern.compile("\\d*(\\.\\d?)?");
|
||||||
|
|
||||||
|
private final AvdInfo mAvd;
|
||||||
|
private final String mSdkLocation;
|
||||||
|
private final SettingsController mSettingsController;
|
||||||
|
|
||||||
|
private Text mScreenSize;
|
||||||
|
private Text mMonitorDpi;
|
||||||
|
private Button mScaleButton;
|
||||||
|
|
||||||
|
private float mScale = 0.f;
|
||||||
|
private boolean mWipeData = false;
|
||||||
|
private int mDensity = 160; // medium density
|
||||||
|
private int mSize1 = -1;
|
||||||
|
private int mSize2 = -1;
|
||||||
|
private String mSkinDisplay;
|
||||||
|
private boolean mEnableScaling = true;
|
||||||
|
|
||||||
|
protected AvdStartDialog(Shell parentShell, AvdInfo avd, String sdkLocation,
|
||||||
|
SettingsController settingsController) {
|
||||||
|
super(parentShell);
|
||||||
|
mAvd = avd;
|
||||||
|
mSdkLocation = sdkLocation;
|
||||||
|
mSettingsController = settingsController;
|
||||||
|
if (mAvd == null) {
|
||||||
|
throw new IllegalArgumentException("avd cannot be null");
|
||||||
|
}
|
||||||
|
if (mSdkLocation == null) {
|
||||||
|
throw new IllegalArgumentException("sdkLocation cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
computeSkinData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getWipeData() {
|
||||||
|
return mWipeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the scaling factor, or 0.f if none are set.
|
||||||
|
*/
|
||||||
|
public float getScale() {
|
||||||
|
return mScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Control createDialogArea(Composite parent) {
|
||||||
|
GridData gd;
|
||||||
|
|
||||||
|
// create a composite with standard margins and spacing
|
||||||
|
Composite composite = new Composite(parent, SWT.NONE);
|
||||||
|
GridLayout layout = new GridLayout(2, false);
|
||||||
|
layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
|
||||||
|
layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
|
||||||
|
layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
|
||||||
|
layout.horizontalSpacing = convertHorizontalDLUsToPixels(
|
||||||
|
IDialogConstants.HORIZONTAL_SPACING);
|
||||||
|
composite.setLayout(layout);
|
||||||
|
composite.setLayoutData(new GridData(GridData.FILL_BOTH));
|
||||||
|
|
||||||
|
Label l = new Label(composite, SWT.NONE);
|
||||||
|
l.setText("Skin:");
|
||||||
|
|
||||||
|
l = new Label(composite, SWT.NONE);
|
||||||
|
l.setText(mSkinDisplay);
|
||||||
|
l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||||
|
|
||||||
|
l = new Label(composite, SWT.NONE);
|
||||||
|
l.setText("Density:");
|
||||||
|
|
||||||
|
l = new Label(composite, SWT.NONE);
|
||||||
|
l.setText(getDensityText());
|
||||||
|
l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||||
|
|
||||||
|
mScaleButton = new Button(composite, SWT.CHECK);
|
||||||
|
mScaleButton.setText("Scale display to real size");
|
||||||
|
mScaleButton.setEnabled(mEnableScaling);
|
||||||
|
boolean defaultState = mEnableScaling && sSkinScaling.get(mAvd.getName()) != null;
|
||||||
|
mScaleButton.setSelection(defaultState);
|
||||||
|
mScaleButton.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||||
|
gd.horizontalSpan = 2;
|
||||||
|
final Group scaleGroup = new Group(composite, SWT.NONE);
|
||||||
|
scaleGroup.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||||
|
gd.horizontalIndent = 30;
|
||||||
|
gd.horizontalSpan = 2;
|
||||||
|
scaleGroup.setLayout(new GridLayout(2, false));
|
||||||
|
|
||||||
|
l = new Label(scaleGroup, SWT.NONE);
|
||||||
|
l.setText("Screen Size (in):");
|
||||||
|
mScreenSize = new Text(scaleGroup, SWT.BORDER);
|
||||||
|
mScreenSize.setText(getScreenSize());
|
||||||
|
mScreenSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||||
|
mScreenSize.addVerifyListener(new VerifyListener() {
|
||||||
|
public void verifyText(VerifyEvent event) {
|
||||||
|
// combine the current content and the new text
|
||||||
|
String text = mScreenSize.getText();
|
||||||
|
text = text.substring(0, event.start) + event.text + text.substring(event.end);
|
||||||
|
|
||||||
|
// now make sure it's a match for the regex
|
||||||
|
event.doit = sScreenSizePattern.matcher(text).matches();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mScreenSize.addModifyListener(new ModifyListener() {
|
||||||
|
public void modifyText(ModifyEvent event) {
|
||||||
|
onScaleChange();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
l = new Label(scaleGroup, SWT.NONE);
|
||||||
|
l.setText("Monitor dpi:");
|
||||||
|
mMonitorDpi = new Text(scaleGroup, SWT.BORDER);
|
||||||
|
mMonitorDpi.setText(Integer.toString(getMonitorDpi()));
|
||||||
|
mMonitorDpi.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
|
||||||
|
mMonitorDpi.addVerifyListener(new VerifyListener() {
|
||||||
|
public void verifyText(VerifyEvent event) {
|
||||||
|
// check for digit only.
|
||||||
|
for (int i = 0 ; i < event.text.length(); i++) {
|
||||||
|
char letter = event.text.charAt(i);
|
||||||
|
if (letter < '0' || letter > '9') {
|
||||||
|
event.doit = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mMonitorDpi.addModifyListener(new ModifyListener() {
|
||||||
|
public void modifyText(ModifyEvent event) {
|
||||||
|
onScaleChange();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
scaleGroup.setEnabled(defaultState);
|
||||||
|
|
||||||
|
mScaleButton.addSelectionListener(new SelectionAdapter() {
|
||||||
|
@Override
|
||||||
|
public void widgetSelected(SelectionEvent event) {
|
||||||
|
boolean enabled = mScaleButton.getSelection();
|
||||||
|
scaleGroup.setEnabled(enabled);
|
||||||
|
if (enabled) {
|
||||||
|
onScaleChange();
|
||||||
|
} else {
|
||||||
|
mScale = 0.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final Button wipeButton = new Button(composite, SWT.CHECK);
|
||||||
|
wipeButton.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||||
|
gd.horizontalSpan = 2;
|
||||||
|
wipeButton.setText("Wipe user data");
|
||||||
|
wipeButton.setSelection(mWipeData = sWipeData);
|
||||||
|
wipeButton.addSelectionListener(new SelectionAdapter() {
|
||||||
|
@Override
|
||||||
|
public void widgetSelected(SelectionEvent arg0) {
|
||||||
|
mWipeData = wipeButton.getSelection();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
l = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL);
|
||||||
|
l.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||||
|
gd.horizontalSpan = 2;
|
||||||
|
|
||||||
|
applyDialogFont(composite);
|
||||||
|
|
||||||
|
// if the scaling is enabled by default, we must initialize the value of mScale
|
||||||
|
if (defaultState) {
|
||||||
|
onScaleChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
return composite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configureShell(Shell newShell) {
|
||||||
|
super.configureShell(newShell);
|
||||||
|
newShell.setText("Launch Options");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Button createButton(Composite parent, int id, String label, boolean defaultButton) {
|
||||||
|
if (id == IDialogConstants.OK_ID) {
|
||||||
|
label = "Launch";
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.createButton(parent, id, label, defaultButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void okPressed() {
|
||||||
|
// override ok to store some info
|
||||||
|
// first the monitor dpi
|
||||||
|
String dpi = mMonitorDpi.getText();
|
||||||
|
if (dpi.length() > 0) {
|
||||||
|
sMonitorDpi = Integer.parseInt(dpi);
|
||||||
|
|
||||||
|
// if there is a setting controller, save it
|
||||||
|
if (mSettingsController != null) {
|
||||||
|
mSettingsController.setMonitorDensity(sMonitorDpi);
|
||||||
|
mSettingsController.saveSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now the scale factor
|
||||||
|
String key = mAvd.getName();
|
||||||
|
sSkinScaling.remove(key);
|
||||||
|
if (mScaleButton.getSelection()) {
|
||||||
|
String size = mScreenSize.getText();
|
||||||
|
if (size.length() > 0) {
|
||||||
|
sSkinScaling.put(key, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// and then the wipe-data checkbox
|
||||||
|
sWipeData = mWipeData;
|
||||||
|
|
||||||
|
// finally continue with the ok action
|
||||||
|
super.okPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void computeSkinData() {
|
||||||
|
Map<String, String> prop = mAvd.getProperties();
|
||||||
|
String dpi = prop.get("hw.lcd.density");
|
||||||
|
if (dpi != null && dpi.length() > 0) {
|
||||||
|
mDensity = Integer.parseInt(dpi);
|
||||||
|
}
|
||||||
|
|
||||||
|
findSkinResolution();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onScaleChange() {
|
||||||
|
String sizeStr = mScreenSize.getText();
|
||||||
|
if (sizeStr.length() == 0) {
|
||||||
|
mScale = 0.f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String dpiStr = mMonitorDpi.getText();
|
||||||
|
if (dpiStr.length() == 0) {
|
||||||
|
mScale = 0.f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dpi = Integer.parseInt(dpiStr);
|
||||||
|
float size = Float.parseFloat(sizeStr);
|
||||||
|
/*
|
||||||
|
* We are trying to emulate the following device:
|
||||||
|
* resolution: 'mSize1'x'mSize2'
|
||||||
|
* density: 'mDensity'
|
||||||
|
* screen diagonal: 'size'
|
||||||
|
* ontop a monitor running at 'dpi'
|
||||||
|
*/
|
||||||
|
// We start by computing the screen diagonal in pixels, if the density was really mDensity
|
||||||
|
float diagonalPx = (float)Math.sqrt(mSize1*mSize1+mSize2*mSize2);
|
||||||
|
// Now we would convert this in actual inches:
|
||||||
|
// diagonalIn = diagonal / mDensity
|
||||||
|
// the scale factor is a mix of adapting to the new density and to the new size.
|
||||||
|
// (size/diagonalIn) * (dpi/mDensity)
|
||||||
|
// this can be simplified to:
|
||||||
|
mScale = (size * dpi) / diagonalPx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the monitor dpi to start with.
|
||||||
|
* This can be coming from the settings, the session-based storage, or the from whatever Java
|
||||||
|
* can tell us.
|
||||||
|
*/
|
||||||
|
private int getMonitorDpi() {
|
||||||
|
if (mSettingsController != null) {
|
||||||
|
sMonitorDpi = mSettingsController.getMonitorDensity();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sMonitorDpi == -1) { // first time? try to get a value
|
||||||
|
sMonitorDpi = Toolkit.getDefaultToolkit().getScreenResolution();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sMonitorDpi;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the screen size to start with.
|
||||||
|
* <p/>If an emulator with the same skin was already launched, scaled, the size used is reused.
|
||||||
|
* <p/>Otherwise the default is returned (3)
|
||||||
|
*/
|
||||||
|
private String getScreenSize() {
|
||||||
|
String size = sSkinScaling.get(mAvd.getName());
|
||||||
|
if (size != null) {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "3";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a display string for the density.
|
||||||
|
*/
|
||||||
|
private String getDensityText() {
|
||||||
|
switch (mDensity) {
|
||||||
|
case 120:
|
||||||
|
return "Low (120)";
|
||||||
|
case 160:
|
||||||
|
return "Medium (160)";
|
||||||
|
case 240:
|
||||||
|
return "High (240)";
|
||||||
|
}
|
||||||
|
|
||||||
|
return Integer.toString(mDensity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the skin resolution and sets it in {@link #mSize1} and {@link #mSize2}.
|
||||||
|
*/
|
||||||
|
private void findSkinResolution() {
|
||||||
|
Map<String, String> prop = mAvd.getProperties();
|
||||||
|
String skinName = prop.get(AvdManager.AVD_INI_SKIN_NAME);
|
||||||
|
|
||||||
|
Matcher m = AvdManager.NUMERIC_SKIN_SIZE.matcher(skinName);
|
||||||
|
if (m.matches()) {
|
||||||
|
mSize1 = Integer.parseInt(m.group(1));
|
||||||
|
mSize2 = Integer.parseInt(m.group(2));
|
||||||
|
mSkinDisplay = skinName;
|
||||||
|
mEnableScaling = true;
|
||||||
|
} else {
|
||||||
|
// The resolution is inside the layout file of the skin.
|
||||||
|
mEnableScaling = false; // default to false for now.
|
||||||
|
|
||||||
|
// path to the skin layout file.
|
||||||
|
File skinFolder = new File(mSdkLocation, prop.get(AvdManager.AVD_INI_SKIN_PATH));
|
||||||
|
if (skinFolder.isDirectory()) {
|
||||||
|
File layoutFile = new File(skinFolder, "layout");
|
||||||
|
if (layoutFile.isFile()) {
|
||||||
|
if (parseLayoutFile(layoutFile)) {
|
||||||
|
mSkinDisplay = String.format("%1$s (%2$dx%3$d)", skinName, mSize1, mSize2);
|
||||||
|
mEnableScaling = true;
|
||||||
|
} else {
|
||||||
|
mSkinDisplay = skinName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a layout file.
|
||||||
|
* <p/>
|
||||||
|
* the format is relatively easy. It's a collection of items defined as
|
||||||
|
* ≶name> {
|
||||||
|
* ≶content>
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* content is either 1+ items or 1+ properties
|
||||||
|
* properties are defined as
|
||||||
|
* ≶name>≶whitespace>≶value>
|
||||||
|
*
|
||||||
|
* We're going to look for an item called display, with 2 properties height and width.
|
||||||
|
* This is very basic parser.
|
||||||
|
*
|
||||||
|
* @param layoutFile the file to parse
|
||||||
|
* @return true if both sizes where found.
|
||||||
|
*/
|
||||||
|
private boolean parseLayoutFile(File layoutFile) {
|
||||||
|
try {
|
||||||
|
BufferedReader input = new BufferedReader(new FileReader(layoutFile));
|
||||||
|
String line;
|
||||||
|
|
||||||
|
while ((line = input.readLine()) != null) {
|
||||||
|
// trim to remove whitespace
|
||||||
|
line = line.trim();
|
||||||
|
int len = line.length();
|
||||||
|
if (len == 0) continue;
|
||||||
|
|
||||||
|
// check if this is a new item
|
||||||
|
if (line.charAt(len-1) == '{') {
|
||||||
|
// this is the start of a node
|
||||||
|
String[] tokens = line.split(" ");
|
||||||
|
if ("display".equals(tokens[0])) {
|
||||||
|
// this is the one we're looking for!
|
||||||
|
while ((mSize1 == -1 || mSize2 == -1) &&
|
||||||
|
(line = input.readLine()) != null) {
|
||||||
|
// trim to remove whitespace
|
||||||
|
line = line.trim();
|
||||||
|
len = line.length();
|
||||||
|
if (len == 0) continue;
|
||||||
|
|
||||||
|
if ("}".equals(line)) { // looks like we're done with the item.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens = line.split(" ");
|
||||||
|
if (tokens.length >= 2) {
|
||||||
|
// there can be multiple space between the name and value
|
||||||
|
// in which case we'll get an extra empty token in the middle.
|
||||||
|
if ("width".equals(tokens[0])) {
|
||||||
|
mSize1 = Integer.parseInt(tokens[tokens.length-1]);
|
||||||
|
} else if ("height".equals(tokens[0])) {
|
||||||
|
mSize2 = Integer.parseInt(tokens[tokens.length-1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mSize1 != -1 && mSize2 != -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// if it reaches here, display was not found.
|
||||||
|
// false is returned below.
|
||||||
|
} catch (IOException e) {
|
||||||
|
// ignore.
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user