/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.sdkuilib.internal.widgets;
import com.android.annotations.Nullable;
import com.android.resources.Density;
import com.android.resources.Keyboard;
import com.android.resources.KeyboardState;
import com.android.resources.Navigation;
import com.android.resources.NavigationState;
import com.android.resources.ResourceEnum;
import com.android.resources.ScreenOrientation;
import com.android.resources.ScreenRatio;
import com.android.resources.ScreenSize;
import com.android.resources.TouchScreen;
import com.android.sdklib.devices.Abi;
import com.android.sdklib.devices.ButtonType;
import com.android.sdklib.devices.Camera;
import com.android.sdklib.devices.CameraLocation;
import com.android.sdklib.devices.Device;
import com.android.sdklib.devices.DeviceManager;
import com.android.sdklib.devices.Hardware;
import com.android.sdklib.devices.Multitouch;
import com.android.sdklib.devices.Network;
import com.android.sdklib.devices.PowerType;
import com.android.sdklib.devices.Screen;
import com.android.sdklib.devices.ScreenType;
import com.android.sdklib.devices.Sensor;
import com.android.sdklib.devices.Software;
import com.android.sdklib.devices.State;
import com.android.sdklib.devices.Storage;
import com.android.sdkuilib.internal.repository.icons.ImageFactory;
import com.android.sdkuilib.ui.GridDataBuilder;
import com.android.sdkuilib.ui.GridDialog;
import com.android.sdkuilib.ui.GridLayoutBuilder;
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.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
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.util.List;
public class DeviceCreationDialog extends GridDialog {
private static final String MANUFACTURER = "User";
private final ImageFactory mImageFactory;
private final DeviceManager mManager;
private List<Device> mUserDevices;
private Device mDevice;
private Text mDeviceName;
private Text mDiagonalLength;
private Text mXDimension;
private Text mYDimension;
private Button mKeyboard;
private Button mDpad;
private Button mTrackball;
private Button mNoNav;
private Text mRam;
private Combo mRamCombo;
private Combo mButtons;
private Combo mSize;
private Combo mDensity;
private Combo mRatio;
private Button mAccelerometer; // hw.accelerometer
private Button mGyro; // hw.sensors.orientation
private Button mGps; // hw.sensors.gps
private Button mProximitySensor; // hw.sensors.proximity
private Button mCameraFront;
private Button mCameraRear;
private Group mStateGroup;
private Button mPortrait;
private Label mPortraitLabel;
private Button mPortraitNav;
private Button mLandscape;
private Label mLandscapeLabel;
private Button mLandscapeNav;
private Button mPortraitKeys;
private Label mPortraitKeysLabel;
private Button mPortraitKeysNav;
private Button mLandscapeKeys;
private Label mLandscapeKeysLabel;
private Button mLandscapeKeysNav;
private Button mForceCreation;
private Label mStatusIcon;
private Label mStatusLabel;
private Button mOkButton;
/** The hardware instance attached to each of the states of the created device. */
private Hardware mHardware;
/** The instance of the Device created by the dialog, if the user pressed {@code mOkButton}. */
private Device mCreatedDevice;
/**
* This contains the Software for the device. Since it has no effect on the
* emulator whatsoever, we just use a single instance with reasonable
* defaults. */
private static final Software mSoftware;
static {
mSoftware = new Software();
mSoftware.setLiveWallpaperSupport(true);
mSoftware.setGlVersion("2.0");
}
public DeviceCreationDialog(Shell parentShell,
DeviceManager manager,
ImageFactory imageFactory,
@Nullable Device device) {
super(parentShell, 3, false);
mImageFactory = imageFactory;
mDevice = device;
mManager = manager;
mUserDevices = mManager.getDevices(DeviceManager.USER_DEVICES);
}
/**
* Returns the instance of the Device created by the dialog,
* if the user pressed the OK|create|edit|clone button.
* Typically only non-null if the dialog returns OK.
*/
public Device getCreatedDevice() {
return mCreatedDevice;
}
@Override
protected Control createContents(Composite parent) {
Control control = super.createContents(parent);
mOkButton = getButton(IDialogConstants.OK_ID);
if (mDevice == null) {
getShell().setText("Create New Device");
} else {
if (mUserDevices.contains(mDevice)) {
getShell().setText("Edit Device");
} else {
getShell().setText("Clone Device");
}
}
Object ld = mOkButton.getLayoutData();
if (ld instanceof GridData) {
((GridData) ld).widthHint = 100;
}
validatePage();
return control;
}
@Override
public void createDialogContent(Composite parent) {
ValidationListener validator = new ValidationListener();
SizeListener sizeListener = new SizeListener();
NavStateListener navListener = new NavStateListener();
Composite column1 = new Composite(parent, SWT.NONE);
GridDataBuilder.create(column1).hFill().vTop();
GridLayoutBuilder.create(column1).columns(2);
// vertical separator between column 1 and 2
Label label = new Label(parent, SWT.SEPARATOR | SWT.VERTICAL);
GridDataBuilder.create(label).vFill().vGrab();
Composite column2 = new Composite(parent, SWT.NONE);
GridDataBuilder.create(column2).hFill().vTop();
GridLayoutBuilder.create(column2).columns(2);
// Column 1
String tooltip = "Name of the new device";
generateLabel("Name:", tooltip, column1);
mDeviceName = generateText(column1, tooltip, new CreateNameModifyListener());
tooltip = "Diagonal length of the screen in inches";
generateLabel("Screen Size (in):", tooltip, column1);
mDiagonalLength = generateText(column1, tooltip, sizeListener);
tooltip = "The resolution of the device in pixels";
generateLabel("Resolution (px):", tooltip, column1);
Composite dimensionGroup = new Composite(column1, SWT.NONE); // Like a Group with no border
GridDataBuilder.create(dimensionGroup).hFill();
GridLayoutBuilder.create(dimensionGroup).columns(3).noMargins();
mXDimension = generateText(dimensionGroup, tooltip, sizeListener);
new Label(dimensionGroup, SWT.NONE).setText("x");
mYDimension = generateText(dimensionGroup, tooltip, sizeListener);
label = new Label(column1, SWT.None); // empty space holder
GridDataBuilder.create(label).hFill().hGrab().hSpan(2);
// Column 2
tooltip = "The screen size bucket that the device falls into";
generateLabel("Size:", tooltip, column2);
mSize = generateCombo(column2, tooltip, ScreenSize.values(), 1, validator);
tooltip = "The aspect ratio bucket the screen falls into. A \"long\" screen is wider.";
generateLabel("Screen Ratio:", tooltip, column2);
mRatio = generateCombo(column2, tooltip, ScreenRatio.values(), 1, validator);
tooltip = "The pixel density bucket the device falls in";
generateLabel("Density:", tooltip, column2);
mDensity = generateCombo(column2, tooltip, Density.values(), 3, validator);
label = new Label(column2, SWT.None); // empty space holder
GridDataBuilder.create(label).hFill().hGrab().hSpan(2);
// Column 1, second row
generateLabel("Sensors:", "The sensors available on the device", column1);
Group sensorGroup = new Group(column1, SWT.NONE);
sensorGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
sensorGroup.setLayout(new GridLayout(2, false));
mAccelerometer = generateButton(sensorGroup, "Accelerometer",
"Presence of an accelerometer", SWT.CHECK, true, validator);
mGyro = generateButton(sensorGroup, "Gyroscope",
"Presence of a gyroscope", SWT.CHECK, true, validator);
mGps = generateButton(sensorGroup, "GPS", "Presence of a GPS", SWT.CHECK, true, validator);
mProximitySensor = generateButton(sensorGroup, "Proximity Sensor",
"Presence of a proximity sensor", SWT.CHECK, true, validator);
generateLabel("Cameras", "The cameras available on the device", column1);
Group cameraGroup = new Group(column1, SWT.NONE);
cameraGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
cameraGroup.setLayout(new GridLayout(2, false));
mCameraFront = generateButton(cameraGroup, "Front", "Presence of a front camera",
SWT.CHECK, false, validator);
mCameraRear = generateButton(cameraGroup, "Rear", "Presence of a rear camera",
SWT.CHECK, true, validator);
generateLabel("Input:", "The input hardware on the given device", column1);
Group inputGroup = new Group(column1, SWT.NONE);
inputGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
inputGroup.setLayout(new GridLayout(3, false));
mKeyboard = generateButton(inputGroup, "Keyboard", "Presence of a hardware keyboard",
SWT.CHECK, false,
new KeyboardListener());
GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
gridData.horizontalSpan = 3;
mKeyboard.setLayoutData(gridData);
mNoNav = generateButton(inputGroup, "No Nav", "No hardware navigation",
SWT.RADIO, true, navListener);
mDpad = generateButton(inputGroup, "DPad", "The device has a DPad navigation element",
SWT.RADIO, false, navListener);
mTrackball = generateButton(inputGroup, "Trackball",
"The device has a trackball navigation element", SWT.RADIO, false, navListener);
tooltip = "The amount of RAM on the device";
generateLabel("RAM:", tooltip, column1);
Group ramGroup = new Group(column1, SWT.NONE);
ramGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
ramGroup.setLayout(new GridLayout(2, false));
mRam = generateText(ramGroup, tooltip, validator);
mRamCombo = new Combo(ramGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
mRamCombo.setToolTipText(tooltip);
mRamCombo.add("MiB");
mRamCombo.add("GiB");
mRamCombo.select(0);
mRamCombo.addModifyListener(validator);
// Column 2, second row
tooltip = "Type of buttons (Home, Menu, etc.) on the device. "
+ "This can be software buttons like on the Galaxy Nexus, or hardware buttons like "
+ "the capacitive buttons on the Nexus S.";
generateLabel("Buttons:", tooltip, column2);
mButtons = new Combo(column2, SWT.DROP_DOWN | SWT.READ_ONLY);
mButtons.setToolTipText(tooltip);
mButtons.add(ButtonType.SOFT.getDescription());
mButtons.add(ButtonType.HARD.getDescription());
mButtons.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mButtons.select(0);
mButtons.addModifyListener(validator);
generateLabel("Device States:", "The available states for the given device", column2);
mStateGroup = new Group(column2, SWT.NONE);
mStateGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mStateGroup.setLayout(new GridLayout(2, true));
tooltip = "The device has a portait position with no keyboard available";
mPortraitLabel = generateLabel("Portrait:", tooltip, mStateGroup);
gridData = new GridData(GridData.FILL_HORIZONTAL);
gridData.horizontalSpan = 2;
mPortraitLabel.setLayoutData(gridData);
mPortrait = generateButton(mStateGroup, "Enabled", tooltip, SWT.CHECK, true,
navListener);
mPortraitNav = generateButton(mStateGroup, "Navigation",
"Hardware navigation is available in this state", SWT.CHECK, true, validator);
mPortraitNav.setEnabled(false);
tooltip = "The device has a landscape position with no keyboard available";
mLandscapeLabel = generateLabel("Landscape:", tooltip, mStateGroup);
gridData = new GridData(GridData.FILL_HORIZONTAL);
gridData.horizontalSpan = 2;
mLandscapeLabel.setLayoutData(gridData);
mLandscape = generateButton(mStateGroup, "Enabled", tooltip, SWT.CHECK, true,
navListener);
mLandscapeNav = generateButton(mStateGroup, "Navigation",
"Hardware navigation is available in this state", SWT.CHECK, true, validator);
mLandscapeNav.setEnabled(false);
tooltip = "The device has a portait position with a keyboard available";
mPortraitKeysLabel = generateLabel("Portrait with keyboard:", tooltip, mStateGroup);
gridData = new GridData(GridData.FILL_HORIZONTAL);
gridData.horizontalSpan = 2;
mPortraitKeysLabel.setLayoutData(gridData);
mPortraitKeysLabel.setEnabled(false);
mPortraitKeys = generateButton(mStateGroup, "Enabled", tooltip, SWT.CHECK, true,
navListener);
mPortraitKeys.setEnabled(false);
mPortraitKeysNav = generateButton(mStateGroup, "Navigation",
"Hardware navigation is available in this state", SWT.CHECK, true, validator);
mPortraitKeysNav.setEnabled(false);
tooltip = "The device has a landscape position with the keyboard open";
mLandscapeKeysLabel = generateLabel("Landscape with keyboard:", tooltip, mStateGroup);
gridData = new GridData(GridData.FILL_HORIZONTAL);
gridData.horizontalSpan = 2;
mLandscapeKeysLabel.setLayoutData(gridData);
mLandscapeKeysLabel.setEnabled(false);
mLandscapeKeys = generateButton(mStateGroup, "Enabled", tooltip, SWT.CHECK, true,
navListener);
mLandscapeKeys.setEnabled(false);
mLandscapeKeysNav = generateButton(mStateGroup, "Navigation",
"Hardware navigation is available in this state", SWT.CHECK, true, validator);
mLandscapeKeysNav.setEnabled(false);
mForceCreation = new Button(column2, SWT.CHECK);
mForceCreation.setText("Override the existing device with the same name");
mForceCreation.setToolTipText("There's already an AVD with the same name. Check this to delete it and replace it by the new AVD.");
mForceCreation.setLayoutData(new GridData(GridData.BEGINNING, GridData.CENTER,
true, false, 2, 1));
mForceCreation.setEnabled(false);
mForceCreation.addSelectionListener(validator);
// -- third row
// add a separator to separate from the ok/cancel button
label = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
label.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false, 3, 1));
// add stuff for the error display
Composite statusComposite = new Composite(parent, SWT.NONE);
GridLayout gl;
statusComposite.setLayoutData(
new GridData(GridData.FILL, GridData.CENTER, true, false, 3, 1));
statusComposite.setLayout(gl = new GridLayout(2, false));
gl.marginHeight = gl.marginWidth = 0;
mStatusIcon = new Label(statusComposite, SWT.NONE);
mStatusIcon.setLayoutData(new GridData(GridData.BEGINNING, GridData.BEGINNING,
false, false));
mStatusLabel = new Label(statusComposite, SWT.NONE);
mStatusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mStatusLabel.setText(""); //$NON-NLS-1$
prefillWithDevice(mDevice);
validatePage();
}
private Button generateButton(Composite parent, String text, String tooltip, int type,
boolean selected, SelectionListener listener) {
Button b = new Button(parent, type);
b.setText(text);
b.setToolTipText(tooltip);
b.setSelection(selected);
b.addSelectionListener(listener);
b.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
return b;
}
/**
* Generates a combo widget attached to the given parent, then sets the
* tooltip, adds all of the {@link String}s returned by
* {@link ResourceEnum#getResourceValue()} for each {@link ResourceEnum},
* sets the combo to the given index and adds the given
* {@link ModifyListener}.
*/
private Combo generateCombo(Composite parent, String tooltip, ResourceEnum[] values,
int selection,
ModifyListener validator) {
Combo c = new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY);
c.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
c.setToolTipText(tooltip);
for (ResourceEnum r : values) {
c.add(r.getResourceValue());
}
c.select(selection);
c.addModifyListener(validator);
return c;
}
/** Generates a text widget with the given tooltip, parent and listener */
private Text generateText(Composite parent, String tooltip, ModifyListener listener) {
Text t = new Text(parent, SWT.BORDER);
t.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
t.setToolTipText(tooltip);
t.addModifyListener(listener);
return t;
}
/** Generates a label and attaches it to the given parent */
private Label generateLabel(String text, String tooltip, Composite parent) {
Label label = new Label(parent, SWT.NONE);
label.setText(text);
label.setToolTipText(tooltip);
label.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_CENTER));
return label;
}
/**
* Callback when the device name is changed. Enforces that device names
* don't conflict with already existing devices unless we're editing that
* device.
*/
private class CreateNameModifyListener implements ModifyListener {
@Override
public void modifyText(ModifyEvent e) {
String name = mDeviceName.getText();
boolean nameCollision = false;
for (Device d : mUserDevices) {
if (MANUFACTURER.equals(d.getManufacturer()) && name.equals(d.getName())) {
nameCollision = true;
break;
}
}
mForceCreation.setEnabled(nameCollision);
mForceCreation.setSelection(!nameCollision);
validatePage();
}
}
/**
* Callback attached to the diagonal length and resolution text boxes. Sets
* the screen size and display density based on their values, then validates
* the page.
*/
private class SizeListener implements ModifyListener {
@Override
public void modifyText(ModifyEvent e) {
if (!mDiagonalLength.getText().isEmpty()) {
try {
double diagonal = Double.parseDouble(mDiagonalLength.getText());
double diagonalDp = 160.0 * diagonal;
// Set the Screen Size
if (diagonalDp >= 1200) {
mSize.select(ScreenSize.getIndex(ScreenSize.getEnum("xlarge")));
} else if (diagonalDp >= 800) {
mSize.select(ScreenSize.getIndex(ScreenSize.getEnum("large")));
} else if (diagonalDp >= 568) {
mSize.select(ScreenSize.getIndex(ScreenSize.getEnum("normal")));
} else {
mSize.select(ScreenSize.getIndex(ScreenSize.getEnum("small")));
}
if (!mXDimension.getText().isEmpty() && !mYDimension.getText().isEmpty()) {
// Set the density based on which bucket it's closest to
double x = Double.parseDouble(mXDimension.getText());
double y = Double.parseDouble(mYDimension.getText());
double dpi = Math.sqrt(x * x + y * y) / diagonal;
double difference = Double.MAX_VALUE;
Density bucket = Density.MEDIUM;
for (Density d : Density.values()) {
if (Math.abs(d.getDpiValue() - dpi) < difference) {
difference = Math.abs(d.getDpiValue() - dpi);
bucket = d;
}
}
mDensity.select(Density.getIndex(bucket));
}
} catch (NumberFormatException ignore) {}
}
}
}
/**
* Callback attached to the keyboard checkbox.Enables / disables device
* states based on the keyboard presence and then validates the page.
*/
private class KeyboardListener extends SelectionAdapter {
@Override
public void widgetSelected(SelectionEvent event) {
super.widgetSelected(event);
if (mKeyboard.getSelection()) {
mPortraitKeys.setEnabled(true);
mPortraitKeysLabel.setEnabled(true);
mLandscapeKeys.setEnabled(true);
mLandscapeKeysLabel.setEnabled(true);
} else {
mPortraitKeys.setEnabled(false);
mPortraitKeysLabel.setEnabled(false);
mLandscapeKeys.setEnabled(false);
mLandscapeKeysLabel.setEnabled(false);
}
toggleNav();
validatePage();
}
}
/**
* Listens for changes on widgets that affect nav availability and toggles
* the nav checkboxes for device states based on them.
*/
private class NavStateListener extends SelectionAdapter {
@Override
public void widgetSelected(SelectionEvent event) {
super.widgetSelected(event);
toggleNav();
validatePage();
}
}
/**
* Method that inspects all of the relevant dialog state and enables or disables the nav
* elements accordingly.
*/
private void toggleNav() {
mPortraitNav.setEnabled(mPortrait.getSelection() && !mNoNav.getSelection());
mLandscapeNav.setEnabled(mLandscape.getSelection() && !mNoNav.getSelection());
mPortraitKeysNav.setEnabled(mPortraitKeys.getSelection() && mPortraitKeys.getEnabled()
&& !mNoNav.getSelection());
mLandscapeKeysNav.setEnabled(mLandscapeKeys.getSelection()
&& mLandscapeKeys.getEnabled() && !mNoNav.getSelection());
validatePage();
}
/**
* Callback that validates the page on modification events or widget
* selections
*/
private class ValidationListener extends SelectionAdapter implements ModifyListener {
@Override
public void modifyText(ModifyEvent e) {
validatePage();
}
@Override
public void widgetSelected(SelectionEvent e) {
super.widgetSelected(e);
validatePage();
}
}
/**
* Validates all of the config options to ensure a valid device can be
* created from them.
*
* @return Whether the config options will result in a valid device.
*/
private boolean validatePage() {
boolean valid = true;
String error = null;
String warning = null;
setError(null);
String name = mDeviceName.getText();
/* If we're editing / cloning a device, this will get called when the name gets pre-filled
* but the ok button won't be populated yet, so we need to skip the initial setting.
*/
if (mOkButton != null) {
if (mDevice == null) {
getShell().setText("Create New Device");
mOkButton.setText("Create Device");
} else {
if (mDevice.getName().equals(name)){
if (mUserDevices.contains(mDevice)) {
getShell().setText("Edit Device");
mOkButton.setText("Edit Device");
} else {
warning = "Only user created devices are editable.\nA clone of it will be created under " +
"the \"User\" category.";
getShell().setText("Clone Device");
mOkButton.setText("Clone Device");
}
} else {
warning = "The device \"" + mDevice.getName() +"\" will be duplicated into\n" +
"\"" + name + "\" under the \"User\" category";
getShell().setText("Clone Device");
mOkButton.setText("Clone Device");
}
}
}
if (valid && name.isEmpty()) {
warning = "Please enter a name for the device.";
valid = false;
}
if (valid && !validateFloat("Diagonal Length", mDiagonalLength.getText())) {
warning = "Please enter a screen size.";
valid = false;
}
if (valid && !validateInt("Resolution", mXDimension.getText())) {
warning = "Please enter the screen resolution.";
valid = false;
}
if (valid && !validateInt("Resolution", mYDimension.getText())) {
warning = "Please enter the screen resolution.";
valid = false;
}
if (valid && mSize.getSelectionIndex() < 0) {
error = "A size bucket must be selected.";
valid = false;
}
if (valid && mDensity.getSelectionIndex() < 0) {
error = "A screen density bucket must be selected";
valid = false;
}
if (valid && mRatio.getSelectionIndex() < 0) {
error = "A screen ratio must be selected.";
valid = false;
}
if (valid && !mNoNav.getSelection() && !mTrackball.getSelection() && !mDpad.getSelection()) {
error = "A mode of hardware navigation, or no navigation, has to be selected.";
valid = false;
}
if (valid && !validateInt("RAM", mRam.getText())) {
warning = "Please enter a RAM amount.";
valid = false;
}
if (valid && mRamCombo.getSelectionIndex() < 0) {
error = "RAM must have a selected unit.";
valid = false;
}
if (valid && mButtons.getSelectionIndex() < 0) {
error = "A button type must be selected.";
valid = false;
}
if (valid) {
if (mKeyboard.getSelection()) {
if (!mPortraitKeys.getSelection()
&& !mPortrait.getSelection()
&& !mLandscapeKeys.getSelection()
&& !mLandscape.getSelection()) {
error = "At least one device state must be enabled.";
valid = false;
}
} else {
if (!mPortrait.getSelection() && !mLandscape.getSelection()) {
error = "At least one device state must be enabled";
valid = false;
}
}
}
if (mForceCreation.isEnabled() && !mForceCreation.getSelection()) {
error = "Name conflicts with an existing device.";
valid = false;
}
if (mOkButton != null) {
mOkButton.setEnabled(valid);
}
if (error != null) {
setError(error);
} else if (warning != null) {
setWarning(warning);
}
return valid;
}
/**
* Validates the string is a valid, positive float. If not, it sets the
* error at the bottom of the dialog and returns false. Note this does
* <b>not</b> unset the error message, it's up to the caller to unset it iff
* it knows there are no errors on the page.
*/
private boolean validateFloat(String box, String value) {
if (value == null || value.isEmpty()) {
return false;
}
boolean ret = true;
try {
double val = Double.parseDouble(value);
if (val <= 0) {
ret = false;
}
} catch (NumberFormatException e) {
ret = false;
}
if (!ret) {
setError(box + " must be a valid, positive number.");
}
return ret;
}
/**
* Validates the string is a valid, positive integer. If not, it sets the
* error at the bottom of the dialog and returns false. Note this does
* <b>not</b> unset the error message, it's up to the caller to unset it iff
* it knows there are no errors on the page.
*/
private boolean validateInt(String box, String value) {
if (value == null || value.isEmpty()) {
return false;
}
boolean ret = true;
try {
int val = Integer.parseInt(value);
if (val <= 0) {
ret = false;
}
} catch (NumberFormatException e) {
ret = false;
}
if (!ret) {
setError(box + " must be a valid, positive integer.");
}
return ret;
}
/**
* Sets the error to the given string. If null, removes the error message.
*/
private void setError(@Nullable String error) {
if (error == null) {
mStatusIcon.setImage(null);
mStatusLabel.setText("");
} else {
mStatusIcon.setImage(mImageFactory.getImageByName("reject_icon16.png"));
mStatusLabel.setText(error);
}
}
/**
* Sets the warning message to the given string. If null, removes the
* warning message.
*/
private void setWarning(@Nullable String warning) {
if (warning == null) {
mStatusIcon.setImage(null);
mStatusLabel.setText("");
} else {
mStatusIcon.setImage(mImageFactory.getImageByName("warning_icon16.png"));
mStatusLabel.setText(warning);
}
}
/** Sets the hardware for the new device */
private void prefillWithDevice(@Nullable Device device) {
if (device == null) {
// Setup the default hardware instance with reasonable values for
// the things which are configurable via this dialog.
mHardware = new Hardware();
Screen s = new Screen();
s.setXdpi(316);
s.setYdpi(316);
s.setMultitouch(Multitouch.JAZZ_HANDS);
s.setMechanism(TouchScreen.FINGER);
s.setScreenType(ScreenType.CAPACITIVE);
mHardware.setScreen(s);
mHardware.addNetwork(Network.BLUETOOTH);
mHardware.addNetwork(Network.WIFI);
mHardware.addNetwork(Network.NFC);
mHardware.addSensor(Sensor.BAROMETER);
mHardware.addSensor(Sensor.COMPASS);
mHardware.addSensor(Sensor.LIGHT_SENSOR);
mHardware.setHasMic(true);
mHardware.addInternalStorage(new Storage(4, Storage.Unit.GiB));
mHardware.setCpu("Generic CPU");
mHardware.setGpu("Generic GPU");
mHardware.addSupportedAbi(Abi.ARMEABI);
mHardware.addSupportedAbi(Abi.ARMEABI_V7A);
mHardware.addSupportedAbi(Abi.MIPS);
mHardware.addSupportedAbi(Abi.X86);
mHardware.setChargeType(PowerType.BATTERY);
return;
}
mHardware = device.getDefaultHardware().deepCopy();
mDeviceName.setText(device.getName());
mForceCreation.setSelection(true);
Screen s = mHardware.getScreen();
mDiagonalLength.setText(Double.toString(s.getDiagonalLength()));
mXDimension.setText(Integer.toString(s.getXDimension()));
mYDimension.setText(Integer.toString(s.getYDimension()));
String size = s.getSize().getResourceValue();
for (int i = 0; i < mSize.getItemCount(); i++) {
if (size.equals(mSize.getItem(i))) {
mSize.select(i);
break;
}
}
String ratio = s.getRatio().getResourceValue();
for (int i = 0; i < mRatio.getItemCount(); i++) {
if (ratio.equals(mRatio.getItem(i))) {
mRatio.select(i);
break;
}
}
String density = s.getPixelDensity().getResourceValue();
for (int i = 0; i < mDensity.getItemCount(); i++) {
if (density.equals(mDensity.getItem(i))) {
mDensity.select(i);
break;
}
}
mKeyboard.setSelection(!Keyboard.NOKEY.equals(mHardware.getKeyboard()));
mDpad.setSelection(Navigation.DPAD.equals(mHardware.getNav()));
mTrackball.setSelection(Navigation.TRACKBALL.equals(mHardware.getNav()));
mNoNav.setSelection(Navigation.NONAV.equals(mHardware.getNav()));
mAccelerometer.setSelection(mHardware.getSensors().contains(Sensor.ACCELEROMETER));
mGyro.setSelection(mHardware.getSensors().contains(Sensor.GYROSCOPE));
mGps.setSelection(mHardware.getSensors().contains(Sensor.GPS));
mProximitySensor.setSelection(mHardware.getSensors().contains(Sensor.PROXIMITY_SENSOR));
mCameraFront.setSelection(false);
mCameraRear.setSelection(false);
for (Camera c : mHardware.getCameras()) {
if (CameraLocation.FRONT.equals(c.getLocation())) {
mCameraFront.setSelection(true);
} else if (CameraLocation.BACK.equals(c.getLocation())) {
mCameraRear.setSelection(true);
}
}
mRam.setText(Long.toString(mHardware.getRam().getSizeAsUnit(Storage.Unit.MiB)));
mRamCombo.select(0);
for (int i = 0; i < mButtons.getItemCount(); i++) {
if (mButtons.getItem(i).equals(mHardware.getButtonType().getDescription())) {
mButtons.select(i);
break;
}
}
for (State state : device.getAllStates()) {
Button nav = null;
if (state.getOrientation().equals(ScreenOrientation.PORTRAIT)) {
if (state.getKeyState().equals(KeyboardState.EXPOSED)) {
mPortraitKeys.setSelection(true);
nav = mPortraitKeysNav;
} else {
mPortrait.setSelection(true);
nav = mPortraitNav;
}
} else {
if (state.getKeyState().equals(KeyboardState.EXPOSED)) {
mLandscapeKeys.setSelection(true);
nav = mLandscapeKeysNav;
} else {
mLandscape.setSelection(true);
nav = mLandscapeNav;
}
}
nav.setSelection(state.getNavState().equals(NavigationState.EXPOSED)
&& !mHardware.getNav().equals(Navigation.NONAV));
}
}
/**
* If given a valid page, generates the corresponding device. The device is
* then added to the user device list, replacing any previous device with
* its given name and manufacturer, and the list is saved out to disk.
*/
@Override
protected void okPressed() {
if (validatePage()) {
Device.Builder builder = new Device.Builder();
builder.setManufacturer("User");
builder.setName(mDeviceName.getText());
builder.addSoftware(mSoftware);
Screen s = mHardware.getScreen();
double diagonal = Double.parseDouble(mDiagonalLength.getText());
int x = Integer.parseInt(mXDimension.getText());
int y = Integer.parseInt(mYDimension.getText());
s.setDiagonalLength(diagonal);
s.setXDimension(x);
s.setYDimension(y);
// The diagonal DPI will be somewhere in between the X and Y dpi if
// they differ
double dpi = Math.sqrt(x * x + y * y) / diagonal;
s.setXdpi(dpi);
s.setYdpi(dpi);
s.setPixelDensity(Density.getEnum(mDensity.getText()));
s.setSize(ScreenSize.getEnum(mSize.getText()));
s.setRatio(ScreenRatio.getEnum(mRatio.getText()));
if (mAccelerometer.getSelection()) {
mHardware.addSensor(Sensor.ACCELEROMETER);
}
if (mGyro.getSelection()) {
mHardware.addSensor(Sensor.GYROSCOPE);
}
if (mGps.getSelection()) {
mHardware.addSensor(Sensor.GPS);
}
if (mProximitySensor.getSelection()) {
mHardware.addSensor(Sensor.PROXIMITY_SENSOR);
}
if (mCameraFront.getSelection()) {
Camera c = new Camera();
c.setAutofocus(true);
c.setFlash(true);
c.setLocation(CameraLocation.FRONT);
mHardware.addCamera(c);
}
if (mCameraRear.getSelection()) {
Camera c = new Camera();
c.setAutofocus(true);
c.setFlash(true);
c.setLocation(CameraLocation.BACK);
mHardware.addCamera(c);
}
if (mKeyboard.getSelection()) {
mHardware.setKeyboard(Keyboard.QWERTY);
} else {
mHardware.setKeyboard(Keyboard.NOKEY);
}
if (mDpad.getSelection()) {
mHardware.setNav(Navigation.DPAD);
} else if (mTrackball.getSelection()) {
mHardware.setNav(Navigation.TRACKBALL);
} else {
mHardware.setNav(Navigation.NONAV);
}
long ram = Long.parseLong(mRam.getText());
Storage.Unit unit = Storage.Unit.getEnum(mRamCombo.getText());
mHardware.setRam(new Storage(ram, unit));
if (mButtons.getText().equals(ButtonType.HARD.getDescription())) {
mHardware.setButtonType(ButtonType.HARD);
} else {
mHardware.setButtonType(ButtonType.SOFT);
}
// Set the first enabled state to the default state
boolean defaultSelected = false;
if (mPortrait.getSelection()) {
State state = new State();
state.setName("Portrait");
state.setDescription("The device in portrait orientation");
state.setOrientation(ScreenOrientation.PORTRAIT);
if (mHardware.getNav().equals(Navigation.NONAV) || !mPortraitNav.getSelection()) {
state.setNavState(NavigationState.HIDDEN);
} else {
state.setNavState(NavigationState.EXPOSED);
}
if (mHardware.getKeyboard().equals(Keyboard.NOKEY)) {
state.setKeyState(KeyboardState.SOFT);
} else {
state.setKeyState(KeyboardState.HIDDEN);
}
state.setHardware(mHardware);
if (!defaultSelected) {
state.setDefaultState(true);
defaultSelected = true;
}
builder.addState(state);
}
if (mLandscape.getSelection()) {
State state = new State();
state.setName("Landscape");
state.setDescription("The device in landscape orientation");
state.setOrientation(ScreenOrientation.LANDSCAPE);
if (mHardware.getNav().equals(Navigation.NONAV) || !mLandscapeNav.getSelection()) {
state.setNavState(NavigationState.HIDDEN);
} else {
state.setNavState(NavigationState.EXPOSED);
}
if (mHardware.getKeyboard().equals(Keyboard.NOKEY)) {
state.setKeyState(KeyboardState.SOFT);
} else {
state.setKeyState(KeyboardState.HIDDEN);
}
state.setHardware(mHardware);
if (!defaultSelected) {
state.setDefaultState(true);
defaultSelected = true;
}
builder.addState(state);
}
if (mKeyboard.getSelection()) {
if (mPortraitKeys.getSelection()) {
State state = new State();
state.setName("Portrait with keyboard");
state.setDescription("The device in portrait orientation with a keyboard open");
state.setOrientation(ScreenOrientation.LANDSCAPE);
if (mHardware.getNav().equals(Navigation.NONAV)
|| !mPortraitKeysNav.getSelection()) {
state.setNavState(NavigationState.HIDDEN);
} else {
state.setNavState(NavigationState.EXPOSED);
}
state.setKeyState(KeyboardState.EXPOSED);
state.setHardware(mHardware);
if (!defaultSelected) {
state.setDefaultState(true);
defaultSelected = true;
}
builder.addState(state);
}
if (mLandscapeKeys.getSelection()) {
State state = new State();
state.setName("Landscape with keyboard");
state.setDescription("The device in landscape orientation with a keyboard open");
state.setOrientation(ScreenOrientation.LANDSCAPE);
if (mHardware.getNav().equals(Navigation.NONAV)
|| !mLandscapeKeysNav.getSelection()) {
state.setNavState(NavigationState.HIDDEN);
} else {
state.setNavState(NavigationState.EXPOSED);
}
state.setKeyState(KeyboardState.EXPOSED);
state.setHardware(mHardware);
if (!defaultSelected) {
state.setDefaultState(true);
defaultSelected = true;
}
builder.addState(state);
}
}
Device d = builder.build();
if (mForceCreation.isEnabled() && mForceCreation.getSelection()) {
mManager.replaceUserDevice(d);
} else {
mManager.addUserDevice(d);
}
mManager.saveUserDevices();
mCreatedDevice = d;
super.okPressed();
}
}
}