• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.sdkuilib.internal.widgets;
18 
19 import com.android.annotations.Nullable;
20 import com.android.resources.Density;
21 import com.android.resources.Keyboard;
22 import com.android.resources.KeyboardState;
23 import com.android.resources.Navigation;
24 import com.android.resources.NavigationState;
25 import com.android.resources.ResourceEnum;
26 import com.android.resources.ScreenOrientation;
27 import com.android.resources.ScreenRatio;
28 import com.android.resources.ScreenSize;
29 import com.android.resources.TouchScreen;
30 import com.android.sdklib.devices.Abi;
31 import com.android.sdklib.devices.ButtonType;
32 import com.android.sdklib.devices.Camera;
33 import com.android.sdklib.devices.CameraLocation;
34 import com.android.sdklib.devices.Device;
35 import com.android.sdklib.devices.DeviceManager;
36 import com.android.sdklib.devices.Hardware;
37 import com.android.sdklib.devices.Multitouch;
38 import com.android.sdklib.devices.Network;
39 import com.android.sdklib.devices.PowerType;
40 import com.android.sdklib.devices.Screen;
41 import com.android.sdklib.devices.ScreenType;
42 import com.android.sdklib.devices.Sensor;
43 import com.android.sdklib.devices.Software;
44 import com.android.sdklib.devices.State;
45 import com.android.sdklib.devices.Storage;
46 import com.android.sdkuilib.internal.repository.icons.ImageFactory;
47 import com.android.sdkuilib.ui.GridDialog;
48 
49 import org.eclipse.jface.dialogs.IDialogConstants;
50 import org.eclipse.swt.SWT;
51 import org.eclipse.swt.events.ModifyEvent;
52 import org.eclipse.swt.events.ModifyListener;
53 import org.eclipse.swt.events.SelectionAdapter;
54 import org.eclipse.swt.events.SelectionEvent;
55 import org.eclipse.swt.events.SelectionListener;
56 import org.eclipse.swt.layout.GridData;
57 import org.eclipse.swt.layout.GridLayout;
58 import org.eclipse.swt.widgets.Button;
59 import org.eclipse.swt.widgets.Combo;
60 import org.eclipse.swt.widgets.Composite;
61 import org.eclipse.swt.widgets.Control;
62 import org.eclipse.swt.widgets.Group;
63 import org.eclipse.swt.widgets.Label;
64 import org.eclipse.swt.widgets.Shell;
65 import org.eclipse.swt.widgets.Text;
66 
67 import java.util.List;
68 
69 public class DeviceCreationDialog extends GridDialog {
70 
71     private static final String MANUFACTURER = "User";
72 
73     private final ImageFactory mImageFactory;
74     private final DeviceManager mManager;
75     private List<Device> mUserDevices;
76 
77     private Device mDevice;
78 
79     private Text mDeviceName;
80     private Text mDiagonalLength;
81     private Text mXDimension;
82     private Text mYDimension;
83     private Button mKeyboard;
84     private Button mDpad;
85     private Button mTrackball;
86     private Button mNoNav;
87     private Text mRam;
88     private Combo mRamCombo;
89     private Combo mButtons;
90     private Combo mSize;
91     private Combo mDensity;
92     private Combo mRatio;
93     private Button mAccelerometer; // hw.accelerometer
94     private Button mGyro; // hw.sensors.orientation
95     private Button mGps; // hw.sensors.gps
96     private Button mProximitySensor; // hw.sensors.proximity
97     private Button mCameraFront;
98     private Button mCameraRear;
99     private Group mStateGroup;
100     private Button mPortrait;
101     private Label mPortraitLabel;
102     private Button mPortraitNav;
103     private Button mLandscape;
104     private Label mLandscapeLabel;
105     private Button mLandscapeNav;
106     private Button mPortraitKeys;
107     private Label mPortraitKeysLabel;
108     private Button mPortraitKeysNav;
109     private Button mLandscapeKeys;
110     private Label mLandscapeKeysLabel;
111     private Button mLandscapeKeysNav;
112 
113     private Button mForceCreation;
114     private Label mStatusIcon;
115     private Label mStatusLabel;
116 
117     private Button mOkButton;
118 
119     // The hardware instance attached to each of the states of the created
120     // device
121     private Hardware mHardware;
122     // This contains the Software for the device. Since it has no effect on the
123     // emulator whatsoever, we just use a single instance with reasonable
124     // defaults.
125     private static final Software mSoftware;
126 
127     static {
128         mSoftware = new Software();
129         mSoftware.setLiveWallpaperSupport(true);
130         mSoftware.setGlVersion("2.0");
131     }
132 
DeviceCreationDialog(Shell parentShell, DeviceManager manager, ImageFactory imageFactory, @Nullable Device device)133     public DeviceCreationDialog(Shell parentShell,
134             DeviceManager manager,
135             ImageFactory imageFactory,
136             @Nullable Device device) {
137         super(parentShell, 2, false);
138         mImageFactory = imageFactory;
139         mDevice = device;
140         mManager = manager;
141         mUserDevices = mManager.getUserDevices();
142     }
143 
144     @Override
createContents(Composite parent)145     protected Control createContents(Composite parent) {
146         Control control = super.createContents(parent);
147 
148         mOkButton = getButton(IDialogConstants.OK_ID);
149 
150         if (mDevice == null) {
151             getShell().setText("Create New Device");
152         } else {
153             if (mUserDevices.contains(mDevice)) {
154                 getShell().setText("Edit Device");
155             } else {
156                 getShell().setText("Clone Device");
157             }
158         }
159 
160         validatePage();
161 
162         return control;
163     }
164 
165     @Override
createDialogContent(Composite parent)166     public void createDialogContent(Composite parent) {
167 
168         ValidationListener validator = new ValidationListener();
169         SizeListener sizeListener = new SizeListener();
170         NavStateListener navListener = new NavStateListener();
171 
172         String tooltip = "Name of the new device";
173         generateLabel("Name:", tooltip, parent);
174         mDeviceName = generateText(parent, tooltip, new CreateNameModifyListener());
175 
176         tooltip = "Diagonal length of the screen in inches";
177         generateLabel("Screen Size (in):", tooltip, parent);
178         mDiagonalLength = generateText(parent, tooltip, sizeListener);
179 
180         tooltip = "The resolution of the device in pixels";
181         generateLabel("Resolution:", tooltip, parent);
182         Group dimensionGroup = new Group(parent, SWT.NONE);
183         dimensionGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
184         dimensionGroup.setLayout(new GridLayout(3, false));
185         mXDimension = generateText(dimensionGroup, tooltip, sizeListener);
186         new Label(dimensionGroup, SWT.NONE).setText("x");
187         mYDimension = generateText(dimensionGroup, tooltip, sizeListener);
188 
189         tooltip = "The screen size bucket that the device falls into";
190         generateLabel("Size:", tooltip, parent);
191         mSize = generateCombo(parent, tooltip, ScreenSize.values(), 1, validator);
192 
193         tooltip = "The aspect ratio bucket the screen falls into. A \"long\" screen is wider.";
194         generateLabel("Screen Ratio:", tooltip, parent);
195         mRatio = generateCombo(parent, tooltip, ScreenRatio.values(), 1, validator);
196 
197         tooltip = "The pixel density bucket the device falls in";
198         generateLabel("Density:", tooltip, parent);
199         mDensity = generateCombo(parent, tooltip, Density.values(), 3, validator);
200 
201         generateLabel("Sensors:", "The sensors available on the device", parent);
202         Group sensorGroup = new Group(parent, SWT.NONE);
203         sensorGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
204         sensorGroup.setLayout(new GridLayout(2, false));
205         mAccelerometer = generateButton(sensorGroup, "Accelerometer",
206                 "Presence of an accelerometer", SWT.CHECK, true, validator);
207         mGyro = generateButton(sensorGroup, "Gyroscope",
208                 "Presence of a gyroscope", SWT.CHECK, true, validator);
209         mGps = generateButton(sensorGroup, "GPS", "Presence of a GPS", SWT.CHECK, true, validator);
210         mProximitySensor = generateButton(sensorGroup, "Proximity Sensor",
211                 "Presence of a proximity sensor", SWT.CHECK, true, validator);
212 
213         generateLabel("Cameras", "The cameras available on the device", parent);
214         Group cameraGroup = new Group(parent, SWT.NONE);
215         cameraGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
216         cameraGroup.setLayout(new GridLayout(2, false));
217         mCameraFront = generateButton(cameraGroup, "Front", "Presence of a front camera",
218                 SWT.CHECK, false, validator);
219         mCameraRear = generateButton(cameraGroup, "Rear", "Presence of a rear camera",
220                 SWT.CHECK, true, validator);
221 
222         generateLabel("Input:", "The input hardware on the given device", parent);
223         Group inputGroup = new Group(parent, SWT.NONE);
224         inputGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
225         inputGroup.setLayout(new GridLayout(3, false));
226         mKeyboard = generateButton(inputGroup, "Keyboard", "Presence of a hardware keyboard",
227                 SWT.CHECK, false,
228                 new KeyboardListener());
229         GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
230         gridData.horizontalSpan = 3;
231         mKeyboard.setLayoutData(gridData);
232         mNoNav = generateButton(inputGroup, "No Nav", "No hardware navigation",
233                 SWT.RADIO, true, navListener);
234         mDpad = generateButton(inputGroup, "DPad", "The device has a DPad navigation element",
235                 SWT.RADIO, false, navListener);
236         mTrackball = generateButton(inputGroup, "Trackball",
237                 "The device has a trackball navigation element", SWT.RADIO, false, navListener);
238 
239         tooltip = "The amount of RAM on the device";
240         generateLabel("RAM:", tooltip, parent);
241         Group ramGroup = new Group(parent, SWT.NONE);
242         ramGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
243         ramGroup.setLayout(new GridLayout(2, false));
244         mRam = generateText(ramGroup, tooltip, validator);
245         mRamCombo = new Combo(ramGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
246         mRamCombo.setToolTipText(tooltip);
247         mRamCombo.add("MiB");
248         mRamCombo.add("GiB");
249         mRamCombo.select(0);
250         mRamCombo.addModifyListener(validator);
251 
252         tooltip = "Type of buttons (Home, Menu, etc.) on the device. "
253                 + "This can be software buttons like on the Galaxy Nexus, or hardware buttons like "
254                 + "the capacitive buttons on the Nexus S.";
255         generateLabel("Buttons:", tooltip, parent);
256         mButtons = new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY);
257         mButtons.setToolTipText(tooltip);
258         mButtons.add("Software");
259         mButtons.add("Hardware");
260         mButtons.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
261         mButtons.select(0);
262         mButtons.addModifyListener(validator);
263 
264         generateLabel("Device States:", "The available states for the given device", parent);
265 
266         mStateGroup = new Group(parent, SWT.NONE);
267         mStateGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
268         mStateGroup.setLayout(new GridLayout(2, true));
269 
270         tooltip = "The device has a portait position with no keyboard available";
271         mPortraitLabel = generateLabel("Portrait:", tooltip, mStateGroup);
272         gridData = new GridData(GridData.FILL_HORIZONTAL);
273         gridData.horizontalSpan = 2;
274         mPortraitLabel.setLayoutData(gridData);
275         mPortrait = generateButton(mStateGroup, "Enabled", tooltip, SWT.CHECK, true,
276                 navListener);
277         mPortraitNav = generateButton(mStateGroup, "Navigation",
278                 "Hardware navigation is available in this state", SWT.CHECK, true, validator);
279         mPortraitNav.setEnabled(false);
280 
281         tooltip = "The device has a landscape position with no keyboard available";
282         mLandscapeLabel = generateLabel("Landscape:", tooltip, mStateGroup);
283         gridData = new GridData(GridData.FILL_HORIZONTAL);
284         gridData.horizontalSpan = 2;
285         mLandscapeLabel.setLayoutData(gridData);
286         mLandscape = generateButton(mStateGroup, "Enabled", tooltip, SWT.CHECK, true,
287                 navListener);
288         mLandscapeNav = generateButton(mStateGroup, "Navigation",
289                 "Hardware navigation is available in this state", SWT.CHECK, true, validator);
290         mLandscapeNav.setEnabled(false);
291 
292         tooltip = "The device has a portait position with a keyboard available";
293         mPortraitKeysLabel = generateLabel("Portrait with keyboard:", tooltip, mStateGroup);
294         gridData = new GridData(GridData.FILL_HORIZONTAL);
295         gridData.horizontalSpan = 2;
296         mPortraitKeysLabel.setLayoutData(gridData);
297         mPortraitKeysLabel.setEnabled(false);
298         mPortraitKeys = generateButton(mStateGroup, "Enabled", tooltip, SWT.CHECK, true,
299                 navListener);
300         mPortraitKeys.setEnabled(false);
301         mPortraitKeysNav = generateButton(mStateGroup, "Navigation",
302                 "Hardware navigation is available in this state", SWT.CHECK, true, validator);
303         mPortraitKeysNav.setEnabled(false);
304 
305         tooltip = "The device has a landscape position with the keyboard open";
306         mLandscapeKeysLabel = generateLabel("Landscape with keyboard:", tooltip, mStateGroup);
307         gridData = new GridData(GridData.FILL_HORIZONTAL);
308         gridData.horizontalSpan = 2;
309         mLandscapeKeysLabel.setLayoutData(gridData);
310         mLandscapeKeysLabel.setEnabled(false);
311         mLandscapeKeys = generateButton(mStateGroup, "Enabled", tooltip, SWT.CHECK, true,
312                 navListener);
313         mLandscapeKeys.setEnabled(false);
314         mLandscapeKeysNav = generateButton(mStateGroup, "Navigation",
315                 "Hardware navigation is available in this state", SWT.CHECK, true, validator);
316         mLandscapeKeysNav.setEnabled(false);
317 
318         mForceCreation = new Button(parent, SWT.CHECK);
319         mForceCreation.setText("Override the existing device with the same name");
320         mForceCreation
321                 .setToolTipText("There's already an AVD with the same name. Check this to delete it and replace it by the new AVD.");
322         mForceCreation.setLayoutData(new GridData(GridData.BEGINNING, GridData.CENTER,
323                 true, false, 2, 1));
324         mForceCreation.setEnabled(false);
325         mForceCreation.addSelectionListener(validator);
326 
327         // add a separator to separate from the ok/cancel button
328         Label label = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
329         label.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false, 3, 1));
330 
331         // add stuff for the error display
332         Composite statusComposite = new Composite(parent, SWT.NONE);
333         GridLayout gl;
334         statusComposite.setLayoutData(new GridData(GridData.FILL, GridData.CENTER,
335                 true, false, 3, 1));
336         statusComposite.setLayout(gl = new GridLayout(2, false));
337         gl.marginHeight = gl.marginWidth = 0;
338 
339         mStatusIcon = new Label(statusComposite, SWT.NONE);
340         mStatusIcon.setLayoutData(new GridData(GridData.BEGINNING, GridData.BEGINNING,
341                 false, false));
342         mStatusLabel = new Label(statusComposite, SWT.NONE);
343         mStatusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
344         mStatusLabel.setText(""); //$NON-NLS-1$
345 
346         prefillWithDevice(mDevice);
347 
348         validatePage();
349     }
350 
generateButton(Composite parent, String text, String tooltip, int type, boolean selected, SelectionListener listener)351     private Button generateButton(Composite parent, String text, String tooltip, int type,
352             boolean selected, SelectionListener listener) {
353         Button b = new Button(parent, type);
354         b.setText(text);
355         b.setToolTipText(tooltip);
356         b.setSelection(selected);
357         b.addSelectionListener(listener);
358         b.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
359         return b;
360     }
361 
362     /**
363      * Generates a combo widget attached to the given parent, then sets the
364      * tooltip, adds all of the {@link String}s returned by
365      * {@link ResourceEnum#getResourceValue()} for each {@link ResourceEnum},
366      * sets the combo to the given index and adds the given
367      * {@link ModifyListener}.
368      */
generateCombo(Composite parent, String tooltip, ResourceEnum[] values, int selection, ModifyListener validator)369     private Combo generateCombo(Composite parent, String tooltip, ResourceEnum[] values,
370             int selection,
371             ModifyListener validator) {
372         Combo c = new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY);
373         c.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
374         c.setToolTipText(tooltip);
375         for (ResourceEnum r : values) {
376             c.add(r.getResourceValue());
377         }
378         c.select(selection);
379         c.addModifyListener(validator);
380         return c;
381     }
382 
383     /** Generates a text widget with the given tooltip, parent and listener */
generateText(Composite parent, String tooltip, ModifyListener listener)384     private Text generateText(Composite parent, String tooltip, ModifyListener listener) {
385         Text t = new Text(parent, SWT.BORDER);
386         t.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
387         t.setToolTipText(tooltip);
388         t.addModifyListener(listener);
389         return t;
390     }
391 
392     /** Generates a label and attaches it to the given parent */
generateLabel(String text, String tooltip, Composite parent)393     private Label generateLabel(String text, String tooltip, Composite parent) {
394         Label label = new Label(parent, SWT.NONE);
395         label.setText(text);
396         label.setToolTipText(tooltip);
397         label.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_CENTER));
398         return label;
399     }
400 
401     /**
402      * Callback when the device name is changed. Enforces that device names
403      * don't conflict with already existing devices unless we're editing that
404      * device.
405      */
406     private class CreateNameModifyListener implements ModifyListener {
407         @Override
modifyText(ModifyEvent e)408         public void modifyText(ModifyEvent e) {
409             String name = mDeviceName.getText();
410             boolean nameCollision = false;
411             for (Device d : mUserDevices) {
412                 if (MANUFACTURER.equals(d.getManufacturer()) && name.equals(d.getName())) {
413                     nameCollision = true;
414                     break;
415                 }
416             }
417             mForceCreation.setEnabled(nameCollision);
418             mForceCreation.setSelection(!nameCollision);
419 
420             validatePage();
421         }
422     }
423 
424     /**
425      * Callback attached to the diagonal length and resolution text boxes. Sets
426      * the screen size and display density based on their values, then validates
427      * the page.
428      */
429     private class SizeListener implements ModifyListener {
430         @Override
modifyText(ModifyEvent e)431         public void modifyText(ModifyEvent e) {
432 
433             if (!mDiagonalLength.getText().isEmpty()) {
434                 double diagonal = Double.parseDouble(mDiagonalLength.getText());
435                 double diagonalDp = 160.0 * diagonal;
436 
437                 // Set the Screen Size
438                 if (diagonalDp >= 1200) {
439                     mSize.select(ScreenSize.getIndex(ScreenSize.getEnum("xlarge")));
440                 } else if (diagonalDp >= 800) {
441                     mSize.select(ScreenSize.getIndex(ScreenSize.getEnum("large")));
442                 } else if (diagonalDp >= 568) {
443                     mSize.select(ScreenSize.getIndex(ScreenSize.getEnum("normal")));
444                 } else {
445                     mSize.select(ScreenSize.getIndex(ScreenSize.getEnum("small")));
446                 }
447                 if (!mXDimension.getText().isEmpty() && !mYDimension.getText().isEmpty()) {
448 
449                     // Set the density based on which bucket it's closest to
450                     double x = Double.parseDouble(mXDimension.getText());
451                     double y = Double.parseDouble(mYDimension.getText());
452                     double dpi = Math.sqrt(x * x + y * y) / diagonal;
453                     double difference = Double.MAX_VALUE;
454                     Density bucket = Density.MEDIUM;
455                     for (Density d : Density.values()) {
456                         if (Math.abs(d.getDpiValue() - dpi) < difference) {
457                             difference = Math.abs(d.getDpiValue() - dpi);
458                             bucket = d;
459                         }
460                     }
461                     mDensity.select(Density.getIndex(bucket));
462                 }
463             }
464         }
465     }
466 
467 
468     /**
469      * Callback attached to the keyboard checkbox.Enables / disables device
470      * states based on the keyboard presence and then validates the page.
471      */
472     private class KeyboardListener extends SelectionAdapter {
473         @Override
widgetSelected(SelectionEvent event)474         public void widgetSelected(SelectionEvent event) {
475             super.widgetSelected(event);
476             if (mKeyboard.getSelection()) {
477                 mPortraitKeys.setEnabled(true);
478                 mPortraitKeysLabel.setEnabled(true);
479                 mLandscapeKeys.setEnabled(true);
480                 mLandscapeKeysLabel.setEnabled(true);
481             } else {
482                 mPortraitKeys.setEnabled(false);
483                 mPortraitKeysLabel.setEnabled(false);
484                 mLandscapeKeys.setEnabled(false);
485                 mLandscapeKeysLabel.setEnabled(false);
486             }
487             toggleNav();
488             validatePage();
489         }
490 
491     }
492 
493     /**
494      * Listens for changes on widgets that affect nav availability and toggles
495      * the nav checkboxes for device states based on them.
496      */
497     private class NavStateListener extends SelectionAdapter {
498         @Override
widgetSelected(SelectionEvent event)499         public void widgetSelected(SelectionEvent event) {
500             super.widgetSelected(event);
501             toggleNav();
502             validatePage();
503         }
504     }
505 
506     /**
507      * Method that inspects all of the relevant dialog state and enables or disables the nav
508      * elements accordingly.
509      */
toggleNav()510     private void toggleNav() {
511         mPortraitNav.setEnabled(mPortrait.getSelection() && !mNoNav.getSelection());
512         mLandscapeNav.setEnabled(mLandscape.getSelection() && !mNoNav.getSelection());
513         mPortraitKeysNav.setEnabled(mPortraitKeys.getSelection() && mPortraitKeys.getEnabled()
514                 && !mNoNav.getSelection());
515         mLandscapeKeysNav.setEnabled(mLandscapeKeys.getSelection()
516                 && mLandscapeKeys.getEnabled() && !mNoNav.getSelection());
517         validatePage();
518     }
519 
520     /**
521      * Callback that validates the page on modification events or widget
522      * selections
523      */
524     private class ValidationListener extends SelectionAdapter implements ModifyListener {
525         @Override
modifyText(ModifyEvent e)526         public void modifyText(ModifyEvent e) {
527             validatePage();
528         }
529 
530         @Override
widgetSelected(SelectionEvent e)531         public void widgetSelected(SelectionEvent e) {
532             super.widgetSelected(e);
533             validatePage();
534         }
535     }
536 
537     /**
538      * Validates all of the config options to ensure a valid device can be
539      * created from them.
540      *
541      * @return Whether the config options will result in a valid device.
542      */
validatePage()543     private boolean validatePage() {
544         boolean valid = true;
545         String error = null;
546         String warning = null;
547         setError(null);
548 
549         String name = mDeviceName.getText();
550 
551         /* If we're editing / cloning a device, this will get called when the name gets pre-filled
552          * but the ok button won't be populated yet, so we need to skip the initial setting.
553          */
554         if (mOkButton != null) {
555             if (mDevice == null) {
556                 getShell().setText("Create New Device");
557                 mOkButton.setText("Create Device");
558             } else {
559                 if (mDevice.getName().equals(name)){
560                     if (mUserDevices.contains(mDevice)) {
561                         getShell().setText("Edit Device");
562                         mOkButton.setText("Edit Device");
563                     } else {
564                         warning = "Only user created devices are editable.\nA clone of it will be created under " +
565                         "the \"User\" category.";
566                         getShell().setText("Clone Device");
567                         mOkButton.setText("Clone Device");
568                     }
569                 } else {
570                     warning = "The device \"" + mDevice.getName() +"\" will be duplicated into\n" +
571                             "\"" + name + "\" under the \"User\" category";
572                     getShell().setText("Clone Device");
573                     mOkButton.setText("Clone Device");
574                 }
575             }
576         }
577 
578         if (name.isEmpty()) {
579             valid = false;
580         }
581         if (!validateFloat("Diagonal Length", mDiagonalLength.getText())) {
582             valid = false;
583         }
584         if (!validateInt("Resolution", mXDimension.getText())) {
585             valid = false;
586         }
587         if (!validateInt("Resolution", mYDimension.getText())) {
588             valid = false;
589         }
590         if (mSize.getSelectionIndex() < 0) {
591             error = "A size bucket must be selected.";
592             valid = false;
593         }
594         if (mDensity.getSelectionIndex() < 0) {
595             error = "A screen density bucket must be selected";
596             valid = false;
597         }
598         if (mRatio.getSelectionIndex() < 0) {
599             error = "A screen ratio must be selected.";
600             valid = false;
601         }
602         if (!mNoNav.getSelection() && !mTrackball.getSelection() && !mDpad.getSelection()) {
603             error = "A mode of hardware navigation, or no navigation, has to be selected.";
604             valid = false;
605         }
606         if (!validateInt("RAM", mRam.getText())) {
607             valid = false;
608         }
609         if (mRamCombo.getSelectionIndex() < 0) {
610             error = "RAM must have a selected unit.";
611             valid = false;
612         }
613         if (mButtons.getSelectionIndex() < 0) {
614             error = "A button type must be selected.";
615             valid = false;
616         }
617         if (mKeyboard.getSelection()) {
618             if (!mPortraitKeys.getSelection()
619                     && !mPortrait.getSelection()
620                     && !mLandscapeKeys.getSelection()
621                     && !mLandscape.getSelection()) {
622                 error = "At least one device state must be enabled.";
623                 valid = false;
624             }
625         } else {
626             if (!mPortrait.getSelection() && !mLandscape.getSelection()) {
627                 error = "At least one device state must be enabled";
628                 valid = false;
629             }
630         }
631         if (mForceCreation.isEnabled() && !mForceCreation.getSelection()) {
632             error = "Name conflicts with an existing device.";
633             valid = false;
634         }
635 
636         if (mOkButton != null) {
637             mOkButton.setEnabled(valid);
638         }
639 
640         if (error != null) {
641             setError(error);
642         } else if (warning != null) {
643             setWarning(warning);
644         }
645 
646         return valid;
647     }
648 
649     /**
650      * Validates the string is a valid, positive float. If not, it sets the
651      * error at the bottom of the dialog and returns false. Note this does
652      * <b>not</b> unset the error message, it's up to the caller to unset it iff
653      * it knows there are no errors on the page.
654      */
validateFloat(String box, String value)655     private boolean validateFloat(String box, String value) {
656         if (value == null || value.isEmpty()) {
657             return false;
658         }
659         boolean ret = true;
660         try {
661             double val = Double.parseDouble(value);
662             if (val <= 0) {
663                 ret = false;
664             }
665         } catch (NumberFormatException e) {
666             ret = false;
667         }
668         if (!ret) {
669             setError(box + " must be a valid, positive number.");
670         }
671         return ret;
672     }
673 
674     /**
675      * Validates the string is a valid, positive integer. If not, it sets the
676      * error at the bottom of the dialog and returns false. Note this does
677      * <b>not</b> unset the error message, it's up to the caller to unset it iff
678      * it knows there are no errors on the page.
679      */
validateInt(String box, String value)680     private boolean validateInt(String box, String value) {
681         if (value == null || value.isEmpty()) {
682             return false;
683         }
684         boolean ret = true;
685         try {
686             int val = Integer.parseInt(value);
687             if (val <= 0) {
688                 ret = false;
689             }
690         } catch (NumberFormatException e) {
691             ret = false;
692         }
693 
694         if (!ret) {
695             setError(box + " must be a valid, positive integer.");
696         }
697 
698         return ret;
699     }
700 
701     /**
702      * Sets the error to the given string. If null, removes the error message.
703      */
setError(@ullable String error)704     private void setError(@Nullable String error) {
705         if (error == null) {
706             mStatusIcon.setImage(null);
707             mStatusLabel.setText("");
708         } else {
709             mStatusIcon.setImage(mImageFactory.getImageByName("reject_icon16.png"));
710             mStatusLabel.setText(error);
711         }
712     }
713 
714     /**
715      * Sets the warning message to the given string. If null, removes the
716      * warning message.
717      */
setWarning(@ullable String warning)718     private void setWarning(@Nullable String warning) {
719         if (warning == null) {
720             mStatusIcon.setImage(null);
721             mStatusLabel.setText("");
722         } else {
723             mStatusIcon.setImage(mImageFactory.getImageByName("warning_icon16.png"));
724             mStatusLabel.setText(warning);
725         }
726     }
727 
728     /** Sets the hardware for the new device */
prefillWithDevice(@ullable Device device)729     private void prefillWithDevice(@Nullable Device device) {
730         if (device == null) {
731 
732             // Setup the default hardware instance with reasonable values for
733             // the things which are configurable via this dialog.
734             mHardware = new Hardware();
735 
736             Screen s = new Screen();
737             s.setXdpi(316);
738             s.setYdpi(316);
739             s.setMultitouch(Multitouch.JAZZ_HANDS);
740             s.setMechanism(TouchScreen.FINGER);
741             s.setScreenType(ScreenType.CAPACITIVE);
742             mHardware.setScreen(s);
743 
744             mHardware.addNetwork(Network.BLUETOOTH);
745             mHardware.addNetwork(Network.WIFI);
746             mHardware.addNetwork(Network.NFC);
747 
748             mHardware.addSensor(Sensor.BAROMETER);
749             mHardware.addSensor(Sensor.COMPASS);
750             mHardware.addSensor(Sensor.LIGHT_SENSOR);
751 
752             mHardware.setHasMic(true);
753             mHardware.addInternalStorage(new Storage(4, Storage.Unit.GiB));
754             mHardware.setCpu("Generic CPU");
755             mHardware.setGpu("Generic GPU");
756 
757             mHardware.addSupportedAbi(Abi.ARMEABI);
758             mHardware.addSupportedAbi(Abi.ARMEABI_V7A);
759             mHardware.addSupportedAbi(Abi.MIPS);
760             mHardware.addSupportedAbi(Abi.X86);
761 
762             mHardware.setChargeType(PowerType.BATTERY);
763             return;
764         }
765         mHardware = device.getDefaultHardware().deepCopy();
766         mDeviceName.setText(device.getName());
767         mForceCreation.setSelection(true);
768         Screen s = mHardware.getScreen();
769         mDiagonalLength.setText(Double.toString(s.getDiagonalLength()));
770         mXDimension.setText(Integer.toString(s.getXDimension()));
771         mYDimension.setText(Integer.toString(s.getYDimension()));
772         String size = s.getSize().getResourceValue();
773         for (int i = 0; i < mSize.getItemCount(); i++) {
774             if (size.equals(mSize.getItem(i))) {
775                 mSize.select(i);
776                 break;
777             }
778         }
779         String ratio = s.getRatio().getResourceValue();
780         for (int i = 0; i < mRatio.getItemCount(); i++) {
781             if (ratio.equals(mRatio.getItem(i))) {
782                 mRatio.select(i);
783                 break;
784             }
785         }
786         String density = s.getPixelDensity().getResourceValue();
787         for (int i = 0; i < mDensity.getItemCount(); i++) {
788             if (density.equals(mDensity.getItem(i))) {
789                 mDensity.select(i);
790                 break;
791             }
792         }
793         mKeyboard.setSelection(!Keyboard.NOKEY.equals(mHardware.getKeyboard()));
794         mDpad.setSelection(Navigation.DPAD.equals(mHardware.getNav()));
795         mTrackball.setSelection(Navigation.TRACKBALL.equals(mHardware.getNav()));
796         mNoNav.setSelection(Navigation.NONAV.equals(mHardware.getNav()));
797         mAccelerometer.setSelection(mHardware.getSensors().contains(Sensor.ACCELEROMETER));
798         mGyro.setSelection(mHardware.getSensors().contains(Sensor.GYROSCOPE));
799         mGps.setSelection(mHardware.getSensors().contains(Sensor.GPS));
800         mProximitySensor.setSelection(mHardware.getSensors().contains(Sensor.PROXIMITY_SENSOR));
801         mCameraFront.setSelection(false);
802         mCameraRear.setSelection(false);
803         for (Camera c : mHardware.getCameras()) {
804             if (CameraLocation.FRONT.equals(c.getLocation())) {
805                 mCameraFront.setSelection(true);
806             } else if (CameraLocation.BACK.equals(c.getLocation())) {
807                 mCameraRear.setSelection(true);
808             }
809         }
810         mRam.setText(Long.toString(mHardware.getRam().getSizeAsUnit(Storage.Unit.MiB)));
811         mRamCombo.select(0);
812         for (int i = 0; i < mButtons.getItemCount(); i++) {
813             if (mButtons.getItem(i).equals(mHardware.getButtonType().toString())) {
814                 mButtons.select(i);
815                 break;
816             }
817         }
818 
819         for (State state : device.getAllStates()) {
820             Button nav = null;
821             if (state.getOrientation().equals(ScreenOrientation.PORTRAIT)) {
822                 if (state.getKeyState().equals(KeyboardState.EXPOSED)) {
823                     mPortraitKeys.setSelection(true);
824                     nav = mPortraitKeysNav;
825                 } else {
826                     mPortrait.setSelection(true);
827                     nav = mPortraitNav;
828                 }
829             } else {
830                 if (state.getKeyState().equals(KeyboardState.EXPOSED)) {
831                     mLandscapeKeys.setSelection(true);
832                     nav = mLandscapeKeysNav;
833                 } else {
834                     mLandscape.setSelection(true);
835                     nav = mLandscapeNav;
836                 }
837             }
838             nav.setSelection(state.getNavState().equals(NavigationState.EXPOSED)
839                     && !mHardware.getNav().equals(Navigation.NONAV));
840         }
841     }
842 
843     /**
844      * If given a valid page, generates the corresponding device. The device is
845      * then added to the user device list, replacing any previous device with
846      * its given name and manufacturer, and the list is saved out to disk.
847      */
848     @Override
okPressed()849     protected void okPressed() {
850         if (validatePage()) {
851             Device.Builder builder = new Device.Builder();
852             builder.setManufacturer("User");
853             builder.setName(mDeviceName.getText());
854             builder.addSoftware(mSoftware);
855             Screen s = mHardware.getScreen();
856             double diagonal = Double.parseDouble(mDiagonalLength.getText());
857             int x = Integer.parseInt(mXDimension.getText());
858             int y = Integer.parseInt(mYDimension.getText());
859             s.setDiagonalLength(diagonal);
860             s.setXDimension(x);
861             s.setYDimension(y);
862             // The diagonal DPI will be somewhere in between the X and Y dpi if
863             // they differ
864             double dpi = Math.sqrt(x * x + y * y) / diagonal;
865             s.setXdpi(dpi);
866             s.setYdpi(dpi);
867             s.setPixelDensity(Density.getEnum(mDensity.getText()));
868             s.setSize(ScreenSize.getEnum(mSize.getText()));
869             s.setRatio(ScreenRatio.getEnum(mRatio.getText()));
870             if (mAccelerometer.getSelection()) {
871                 mHardware.addSensor(Sensor.ACCELEROMETER);
872             }
873             if (mGyro.getSelection()) {
874                 mHardware.addSensor(Sensor.GYROSCOPE);
875             }
876             if (mGps.getSelection()) {
877                 mHardware.addSensor(Sensor.GPS);
878             }
879             if (mProximitySensor.getSelection()) {
880                 mHardware.addSensor(Sensor.PROXIMITY_SENSOR);
881             }
882             if (mCameraFront.getSelection()) {
883                 Camera c = new Camera();
884                 c.setAutofocus(true);
885                 c.setFlash(true);
886                 c.setLocation(CameraLocation.FRONT);
887                 mHardware.addCamera(c);
888             }
889             if (mCameraRear.getSelection()) {
890                 Camera c = new Camera();
891                 c.setAutofocus(true);
892                 c.setFlash(true);
893                 c.setLocation(CameraLocation.BACK);
894                 mHardware.addCamera(c);
895             }
896             if (mKeyboard.getSelection()) {
897                 mHardware.setKeyboard(Keyboard.QWERTY);
898             } else {
899                 mHardware.setKeyboard(Keyboard.NOKEY);
900             }
901             if (mDpad.getSelection()) {
902                 mHardware.setNav(Navigation.DPAD);
903             } else if (mTrackball.getSelection()) {
904                 mHardware.setNav(Navigation.TRACKBALL);
905             } else {
906                 mHardware.setNav(Navigation.NONAV);
907             }
908             long ram = Long.parseLong(mRam.getText());
909             Storage.Unit unit = Storage.Unit.getEnum(mRamCombo.getText());
910             mHardware.setRam(new Storage(ram, unit));
911             if (mButtons.getText().equals("Hardware")) {
912                 mHardware.setButtonType(ButtonType.HARD);
913             } else {
914                 mHardware.setButtonType(ButtonType.SOFT);
915             }
916 
917             // Set the first enabled state to the default state
918             boolean defaultSelected = false;
919             if (mPortrait.getSelection()) {
920                 State state = new State();
921                 state.setName("Portrait");
922                 state.setDescription("The device in portrait orientation");
923                 state.setOrientation(ScreenOrientation.PORTRAIT);
924                 if (mHardware.getNav().equals(Navigation.NONAV) || !mPortraitNav.getSelection()) {
925                     state.setNavState(NavigationState.HIDDEN);
926                 } else {
927                     state.setNavState(NavigationState.EXPOSED);
928                 }
929                 if (mHardware.getKeyboard().equals(Keyboard.NOKEY)) {
930                     state.setKeyState(KeyboardState.SOFT);
931                 } else {
932                     state.setKeyState(KeyboardState.HIDDEN);
933                 }
934                 state.setHardware(mHardware);
935                 if (!defaultSelected) {
936                     state.setDefaultState(true);
937                     defaultSelected = true;
938                 }
939                 builder.addState(state);
940             }
941             if (mLandscape.getSelection()) {
942                 State state = new State();
943                 state.setName("Landscape");
944                 state.setDescription("The device in landscape orientation");
945                 state.setOrientation(ScreenOrientation.LANDSCAPE);
946                 if (mHardware.getNav().equals(Navigation.NONAV) || !mLandscapeNav.getSelection()) {
947                     state.setNavState(NavigationState.HIDDEN);
948                 } else {
949                     state.setNavState(NavigationState.EXPOSED);
950                 }
951                 if (mHardware.getKeyboard().equals(Keyboard.NOKEY)) {
952                     state.setKeyState(KeyboardState.SOFT);
953                 } else {
954                     state.setKeyState(KeyboardState.HIDDEN);
955                 }
956                 state.setHardware(mHardware);
957                 if (!defaultSelected) {
958                     state.setDefaultState(true);
959                     defaultSelected = true;
960                 }
961                 builder.addState(state);
962             }
963             if (mKeyboard.getSelection()) {
964                 if (mPortraitKeys.getSelection()) {
965                     State state = new State();
966                     state.setName("Portrait with keyboard");
967                     state.setDescription("The device in portrait orientation with a keyboard open");
968                     state.setOrientation(ScreenOrientation.LANDSCAPE);
969                     if (mHardware.getNav().equals(Navigation.NONAV)
970                             || !mPortraitKeysNav.getSelection()) {
971                         state.setNavState(NavigationState.HIDDEN);
972                     } else {
973                         state.setNavState(NavigationState.EXPOSED);
974                     }
975                     state.setKeyState(KeyboardState.EXPOSED);
976                     state.setHardware(mHardware);
977                     if (!defaultSelected) {
978                         state.setDefaultState(true);
979                         defaultSelected = true;
980                     }
981                     builder.addState(state);
982                 }
983                 if (mLandscapeKeys.getSelection()) {
984                     State state = new State();
985                     state.setName("Landscape with keyboard");
986                     state.setDescription("The device in landscape orientation with a keyboard open");
987                     state.setOrientation(ScreenOrientation.LANDSCAPE);
988                     if (mHardware.getNav().equals(Navigation.NONAV)
989                             || !mLandscapeKeysNav.getSelection()) {
990                         state.setNavState(NavigationState.HIDDEN);
991                     } else {
992                         state.setNavState(NavigationState.EXPOSED);
993                     }
994                     state.setKeyState(KeyboardState.EXPOSED);
995                     state.setHardware(mHardware);
996                     if (!defaultSelected) {
997                         state.setDefaultState(true);
998                         defaultSelected = true;
999                     }
1000                     builder.addState(state);
1001                 }
1002             }
1003             Device d = builder.build();
1004             if (mForceCreation.isEnabled() && mForceCreation.getSelection()) {
1005                 mManager.replaceUserDevice(d);
1006             } else {
1007                 mManager.addUserDevice(d);
1008             }
1009             mManager.saveUserDevices();
1010             super.okPressed();
1011         }
1012     }
1013 
1014 }
1015