/* * Copyright (C) 2007 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.ddmuilib; import com.android.ddmlib.Device; import com.android.ddmlib.EmulatorConsole; import com.android.ddmlib.EmulatorConsole.GsmMode; import com.android.ddmlib.EmulatorConsole.GsmStatus; import com.android.ddmlib.EmulatorConsole.NetworkStatus; import com.android.ddmuilib.location.CoordinateControls; import com.android.ddmuilib.location.GpxParser; import com.android.ddmuilib.location.KmlParser; import com.android.ddmuilib.location.TrackContentProvider; import com.android.ddmuilib.location.TrackLabelProvider; import com.android.ddmuilib.location.TrackPoint; import com.android.ddmuilib.location.WayPoint; import com.android.ddmuilib.location.WayPointContentProvider; import com.android.ddmuilib.location.WayPointLabelProvider; import com.android.ddmuilib.location.GpxParser.Track; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.custom.StackLayout; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; 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.graphics.Image; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FillLayout; 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.Display; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.Text; /** * Panel to control the emulator using EmulatorConsole objects. */ public class EmulatorControlPanel extends SelectionDependentPanel { // default location: Patio outside Charlie's private final static double DEFAULT_LONGITUDE = -122.084095; private final static double DEFAULT_LATITUDE = 37.422006; private final static String SPEED_FORMAT = "Speed: %1$dX"; /** * Map between the display gsm mode and the internal tag used by the display. */ private final static String[][] GSM_MODES = new String[][] { { "unregistered", GsmMode.UNREGISTERED.getTag() }, { "home", GsmMode.HOME.getTag() }, { "roaming", GsmMode.ROAMING.getTag() }, { "searching", GsmMode.SEARCHING.getTag() }, { "denied", GsmMode.DENIED.getTag() }, }; private final static String[] NETWORK_SPEEDS = new String[] { "Full", "GSM", "HSCSD", "GPRS", "EDGE", "UMTS", "HSDPA", }; private final static String[] NETWORK_LATENCIES = new String[] { "None", "GPRS", "EDGE", "UMTS", }; private final static int[] PLAY_SPEEDS = new int[] { 1, 2, 5, 10, 20, 50 }; private final static String RE_PHONE_NUMBER = "^[+#0-9]+$"; //$NON-NLS-1$ private final static String PREFS_WAYPOINT_COL_NAME = "emulatorControl.waypoint.name"; //$NON-NLS-1$ private final static String PREFS_WAYPOINT_COL_LONGITUDE = "emulatorControl.waypoint.longitude"; //$NON-NLS-1$ private final static String PREFS_WAYPOINT_COL_LATITUDE = "emulatorControl.waypoint.latitude"; //$NON-NLS-1$ private final static String PREFS_WAYPOINT_COL_ELEVATION = "emulatorControl.waypoint.elevation"; //$NON-NLS-1$ private final static String PREFS_WAYPOINT_COL_DESCRIPTION = "emulatorControl.waypoint.desc"; //$NON-NLS-1$ private final static String PREFS_TRACK_COL_NAME = "emulatorControl.track.name"; //$NON-NLS-1$ private final static String PREFS_TRACK_COL_COUNT = "emulatorControl.track.count"; //$NON-NLS-1$ private final static String PREFS_TRACK_COL_FIRST = "emulatorControl.track.first"; //$NON-NLS-1$ private final static String PREFS_TRACK_COL_LAST = "emulatorControl.track.last"; //$NON-NLS-1$ private final static String PREFS_TRACK_COL_COMMENT = "emulatorControl.track.comment"; //$NON-NLS-1$ private IImageLoader mImageLoader; private EmulatorConsole mEmulatorConsole; private Composite mParent; private Label mVoiceLabel; private Combo mVoiceMode; private Label mDataLabel; private Combo mDataMode; private Label mSpeedLabel; private Combo mNetworkSpeed; private Label mLatencyLabel; private Combo mNetworkLatency; private Label mNumberLabel; private Text mPhoneNumber; private Button mVoiceButton; private Button mSmsButton; private Label mMessageLabel; private Text mSmsMessage; private Button mCallButton; private Button mCancelButton; private TabFolder mLocationFolders; private Button mDecimalButton; private Button mSexagesimalButton; private CoordinateControls mLongitudeControls; private CoordinateControls mLatitudeControls; private Button mGpxUploadButton; private Table mGpxWayPointTable; private Table mGpxTrackTable; private Button mKmlUploadButton; private Table mKmlWayPointTable; private Button mPlayGpxButton; private Button mGpxBackwardButton; private Button mGpxForwardButton; private Button mGpxSpeedButton; private Button mPlayKmlButton; private Button mKmlBackwardButton; private Button mKmlForwardButton; private Button mKmlSpeedButton; private Image mPlayImage; private Image mPauseImage; private Thread mPlayingThread; private boolean mPlayingTrack; private int mPlayDirection = 1; private int mSpeed; private int mSpeedIndex; private final SelectionAdapter mDirectionButtonAdapter = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Button b = (Button)e.getSource(); if (b.getSelection() == false) { // basically the button was unselected, which we don't allow. // so we reselect it. b.setSelection(true); return; } // now handle selection change. if (b == mGpxForwardButton || b == mKmlForwardButton) { mGpxBackwardButton.setSelection(false); mGpxForwardButton.setSelection(true); mKmlBackwardButton.setSelection(false); mKmlForwardButton.setSelection(true); mPlayDirection = 1; } else { mGpxBackwardButton.setSelection(true); mGpxForwardButton.setSelection(false); mKmlBackwardButton.setSelection(true); mKmlForwardButton.setSelection(false); mPlayDirection = -1; } } }; private final SelectionAdapter mSpeedButtonAdapter = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { mSpeedIndex = (mSpeedIndex+1) % PLAY_SPEEDS.length; mSpeed = PLAY_SPEEDS[mSpeedIndex]; mGpxSpeedButton.setText(String.format(SPEED_FORMAT, mSpeed)); mGpxPlayControls.pack(); mKmlSpeedButton.setText(String.format(SPEED_FORMAT, mSpeed)); mKmlPlayControls.pack(); if (mPlayingThread != null) { mPlayingThread.interrupt(); } } }; private Composite mKmlPlayControls; private Composite mGpxPlayControls; public EmulatorControlPanel(IImageLoader imageLoader) { mImageLoader = imageLoader; } /** * Sent when a new device is selected. The new device can be accessed * with {@link #getCurrentDevice()} */ @Override public void deviceSelected() { handleNewDevice(getCurrentDevice()); } /** * Sent when a new client is selected. The new client can be accessed * with {@link #getCurrentClient()} */ @Override public void clientSelected() { // pass } /** * Creates a control capable of displaying some information. This is * called once, when the application is initializing, from the UI thread. */ @Override protected Control createControl(Composite parent) { mParent = parent; final ScrolledComposite scollingParent = new ScrolledComposite(parent, SWT.V_SCROLL); scollingParent.setExpandVertical(true); scollingParent.setExpandHorizontal(true); scollingParent.setLayoutData(new GridData(GridData.FILL_BOTH)); final Composite top = new Composite(scollingParent, SWT.NONE); scollingParent.setContent(top); top.setLayout(new GridLayout(1, false)); // set the resize for the scrolling to work (why isn't that done automatically?!?) scollingParent.addControlListener(new ControlAdapter() { @Override public void controlResized(ControlEvent e) { Rectangle r = scollingParent.getClientArea(); scollingParent.setMinSize(top.computeSize(r.width, SWT.DEFAULT)); } }); createRadioControls(top); createCallControls(top); createLocationControls(top); doEnable(false); top.layout(); Rectangle r = scollingParent.getClientArea(); scollingParent.setMinSize(top.computeSize(r.width, SWT.DEFAULT)); return scollingParent; } /** * Create Radio (on/off/roaming, for voice/data) controls. * @param top */ private void createRadioControls(final Composite top) { Group g1 = new Group(top, SWT.NONE); g1.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); g1.setLayout(new GridLayout(2, false)); g1.setText("Telephony Status"); // the inside of the group is 2 composite so that all the column of the controls (mainly // combos) have the same width, while not taking the whole screen width Composite insideGroup = new Composite(g1, SWT.NONE); GridLayout gl = new GridLayout(4, false); gl.marginBottom = gl.marginHeight = gl.marginLeft = gl.marginRight = 0; insideGroup.setLayout(gl); mVoiceLabel = new Label(insideGroup, SWT.NONE); mVoiceLabel.setText("Voice:"); mVoiceLabel.setAlignment(SWT.RIGHT); mVoiceMode = new Combo(insideGroup, SWT.READ_ONLY); mVoiceMode.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); for (String[] mode : GSM_MODES) { mVoiceMode.add(mode[0]); } mVoiceMode.addSelectionListener(new SelectionAdapter() { // called when selection changes @Override public void widgetSelected(SelectionEvent e) { setVoiceMode(mVoiceMode.getSelectionIndex()); } }); mSpeedLabel = new Label(insideGroup, SWT.NONE); mSpeedLabel.setText("Speed:"); mSpeedLabel.setAlignment(SWT.RIGHT); mNetworkSpeed = new Combo(insideGroup, SWT.READ_ONLY); mNetworkSpeed.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); for (String mode : NETWORK_SPEEDS) { mNetworkSpeed.add(mode); } mNetworkSpeed.addSelectionListener(new SelectionAdapter() { // called when selection changes @Override public void widgetSelected(SelectionEvent e) { setNetworkSpeed(mNetworkSpeed.getSelectionIndex()); } }); mDataLabel = new Label(insideGroup, SWT.NONE); mDataLabel.setText("Data:"); mDataLabel.setAlignment(SWT.RIGHT); mDataMode = new Combo(insideGroup, SWT.READ_ONLY); mDataMode.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); for (String[] mode : GSM_MODES) { mDataMode.add(mode[0]); } mDataMode.addSelectionListener(new SelectionAdapter() { // called when selection changes @Override public void widgetSelected(SelectionEvent e) { setDataMode(mDataMode.getSelectionIndex()); } }); mLatencyLabel = new Label(insideGroup, SWT.NONE); mLatencyLabel.setText("Latency:"); mLatencyLabel.setAlignment(SWT.RIGHT); mNetworkLatency = new Combo(insideGroup, SWT.READ_ONLY); mNetworkLatency.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); for (String mode : NETWORK_LATENCIES) { mNetworkLatency.add(mode); } mNetworkLatency.addSelectionListener(new SelectionAdapter() { // called when selection changes @Override public void widgetSelected(SelectionEvent e) { setNetworkLatency(mNetworkLatency.getSelectionIndex()); } }); // now an empty label to take the rest of the width of the group Label l = new Label(g1, SWT.NONE); l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); } /** * Create Voice/SMS call/hang up controls * @param top */ private void createCallControls(final Composite top) { GridLayout gl; Group g2 = new Group(top, SWT.NONE); g2.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); g2.setLayout(new GridLayout(1, false)); g2.setText("Telephony Actions"); // horizontal composite for label + text field Composite phoneComp = new Composite(g2, SWT.NONE); phoneComp.setLayoutData(new GridData(GridData.FILL_BOTH)); gl = new GridLayout(2, false); gl.marginBottom = gl.marginHeight = gl.marginLeft = gl.marginRight = 0; phoneComp.setLayout(gl); mNumberLabel = new Label(phoneComp, SWT.NONE); mNumberLabel.setText("Incoming number:"); mPhoneNumber = new Text(phoneComp, SWT.BORDER | SWT.LEFT | SWT.SINGLE); mPhoneNumber.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mPhoneNumber.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { // Reenable the widgets based on the content of the text. // doEnable checks the validity of the phone number to enable/disable some // widgets. // Looks like we're getting a callback at creation time, so we can't // suppose that we are enabled when the text is modified... doEnable(mEmulatorConsole != null); } }); mVoiceButton = new Button(phoneComp, SWT.RADIO); GridData gd = new GridData(); gd.horizontalSpan = 2; mVoiceButton.setText("Voice"); mVoiceButton.setLayoutData(gd); mVoiceButton.setEnabled(false); mVoiceButton.setSelection(true); mVoiceButton.addSelectionListener(new SelectionAdapter() { // called when selection changes @Override public void widgetSelected(SelectionEvent e) { doEnable(true); if (mVoiceButton.getSelection()) { mCallButton.setText("Call"); } else { mCallButton.setText("Send"); } } }); mSmsButton = new Button(phoneComp, SWT.RADIO); mSmsButton.setText("SMS"); gd = new GridData(); gd.horizontalSpan = 2; mSmsButton.setLayoutData(gd); mSmsButton.setEnabled(false); // Since there are only 2 radio buttons, we can put a listener on only one (they // are both called on select and unselect event. mMessageLabel = new Label(phoneComp, SWT.NONE); gd = new GridData(); gd.verticalAlignment = SWT.TOP; mMessageLabel.setLayoutData(gd); mMessageLabel.setText("Message:"); mMessageLabel.setEnabled(false); mSmsMessage = new Text(phoneComp, SWT.BORDER | SWT.LEFT | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL); mSmsMessage.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); gd.heightHint = 70; mSmsMessage.setEnabled(false); // composite to put the 2 buttons horizontally Composite g2ButtonComp = new Composite(g2, SWT.NONE); g2ButtonComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); gl = new GridLayout(2, false); gl.marginWidth = gl.marginHeight = 0; g2ButtonComp.setLayout(gl); // now a button below the phone number mCallButton = new Button(g2ButtonComp, SWT.PUSH); mCallButton.setText("Call"); mCallButton.setEnabled(false); mCallButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (mEmulatorConsole != null) { if (mVoiceButton.getSelection()) { processCommandResult(mEmulatorConsole.call(mPhoneNumber.getText().trim())); } else { // we need to encode the message. We need to replace the carriage return // character by the 2 character string \n. // Because of this the \ character needs to be escaped as well. // ReplaceAll() expects regexp so \ char are escaped twice. String message = mSmsMessage.getText(); message = message.replaceAll("\\\\", //$NON-NLS-1$ "\\\\\\\\"); //$NON-NLS-1$ // While the normal line delimiter is returned by Text.getLineDelimiter() // it seems copy pasting text coming from somewhere else could have another // delimited. For this reason, we'll replace is several steps // replace the dual CR-LF message = message.replaceAll("\r\n", "\\\\n"); //$NON-NLS-1$ //$NON-NLS-1$ // replace remaining stand alone \n message = message.replaceAll("\n", "\\\\n"); //$NON-NLS-1$ //$NON-NLS-1$ // replace remaining stand alone \r message = message.replaceAll("\r", "\\\\n"); //$NON-NLS-1$ //$NON-NLS-1$ processCommandResult(mEmulatorConsole.sendSms(mPhoneNumber.getText().trim(), message)); } } } }); mCancelButton = new Button(g2ButtonComp, SWT.PUSH); mCancelButton.setText("Hang Up"); mCancelButton.setEnabled(false); mCancelButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (mEmulatorConsole != null) { if (mVoiceButton.getSelection()) { processCommandResult(mEmulatorConsole.cancelCall( mPhoneNumber.getText().trim())); } } } }); } /** * Create Location controls. * @param top */ private void createLocationControls(final Composite top) { Label l = new Label(top, SWT.NONE); l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); l.setText("Location Controls"); mLocationFolders = new TabFolder(top, SWT.NONE); mLocationFolders.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); Composite manualLocationComp = new Composite(mLocationFolders, SWT.NONE); TabItem item = new TabItem(mLocationFolders, SWT.NONE); item.setText("Manual"); item.setControl(manualLocationComp); createManualLocationControl(manualLocationComp); mPlayImage = mImageLoader.loadImage("play.png", mParent.getDisplay()); // $NON-NLS-1$ mPauseImage = mImageLoader.loadImage("pause.png", mParent.getDisplay()); // $NON-NLS-1$ Composite gpxLocationComp = new Composite(mLocationFolders, SWT.NONE); item = new TabItem(mLocationFolders, SWT.NONE); item.setText("GPX"); item.setControl(gpxLocationComp); createGpxLocationControl(gpxLocationComp); Composite kmlLocationComp = new Composite(mLocationFolders, SWT.NONE); kmlLocationComp.setLayout(new FillLayout()); item = new TabItem(mLocationFolders, SWT.NONE); item.setText("KML"); item.setControl(kmlLocationComp); createKmlLocationControl(kmlLocationComp); } private void createManualLocationControl(Composite manualLocationComp) { final StackLayout sl; GridLayout gl; Label label; manualLocationComp.setLayout(new GridLayout(1, false)); mDecimalButton = new Button(manualLocationComp, SWT.RADIO); mDecimalButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mDecimalButton.setText("Decimal"); mSexagesimalButton = new Button(manualLocationComp, SWT.RADIO); mSexagesimalButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mSexagesimalButton.setText("Sexagesimal"); // composite to hold and switching between the 2 modes. final Composite content = new Composite(manualLocationComp, SWT.NONE); content.setLayout(sl = new StackLayout()); // decimal display final Composite decimalContent = new Composite(content, SWT.NONE); decimalContent.setLayout(gl = new GridLayout(2, false)); gl.marginHeight = gl.marginWidth = 0; mLongitudeControls = new CoordinateControls(); mLatitudeControls = new CoordinateControls(); label = new Label(decimalContent, SWT.NONE); label.setText("Longitude"); mLongitudeControls.createDecimalText(decimalContent); label = new Label(decimalContent, SWT.NONE); label.setText("Latitude"); mLatitudeControls.createDecimalText(decimalContent); // sexagesimal content final Composite sexagesimalContent = new Composite(content, SWT.NONE); sexagesimalContent.setLayout(gl = new GridLayout(7, false)); gl.marginHeight = gl.marginWidth = 0; label = new Label(sexagesimalContent, SWT.NONE); label.setText("Longitude"); mLongitudeControls.createSexagesimalDegreeText(sexagesimalContent); label = new Label(sexagesimalContent, SWT.NONE); label.setText("\u00B0"); // degree character mLongitudeControls.createSexagesimalMinuteText(sexagesimalContent); label = new Label(sexagesimalContent, SWT.NONE); label.setText("'"); mLongitudeControls.createSexagesimalSecondText(sexagesimalContent); label = new Label(sexagesimalContent, SWT.NONE); label.setText("\""); label = new Label(sexagesimalContent, SWT.NONE); label.setText("Latitude"); mLatitudeControls.createSexagesimalDegreeText(sexagesimalContent); label = new Label(sexagesimalContent, SWT.NONE); label.setText("\u00B0"); mLatitudeControls.createSexagesimalMinuteText(sexagesimalContent); label = new Label(sexagesimalContent, SWT.NONE); label.setText("'"); mLatitudeControls.createSexagesimalSecondText(sexagesimalContent); label = new Label(sexagesimalContent, SWT.NONE); label.setText("\""); // set the default display to decimal sl.topControl = decimalContent; mDecimalButton.setSelection(true); mDecimalButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (mDecimalButton.getSelection()) { sl.topControl = decimalContent; } else { sl.topControl = sexagesimalContent; } content.layout(); } }); Button sendButton = new Button(manualLocationComp, SWT.PUSH); sendButton.setText("Send"); sendButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (mEmulatorConsole != null) { processCommandResult(mEmulatorConsole.sendLocation( mLongitudeControls.getValue(), mLatitudeControls.getValue(), 0)); } } }); mLongitudeControls.setValue(DEFAULT_LONGITUDE); mLatitudeControls.setValue(DEFAULT_LATITUDE); } private void createGpxLocationControl(Composite gpxLocationComp) { GridData gd; IPreferenceStore store = DdmUiPreferences.getStore(); gpxLocationComp.setLayout(new GridLayout(1, false)); mGpxUploadButton = new Button(gpxLocationComp, SWT.PUSH); mGpxUploadButton.setText("Load GPX..."); // Table for way point mGpxWayPointTable = new Table(gpxLocationComp, SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION); mGpxWayPointTable.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); gd.heightHint = 100; mGpxWayPointTable.setHeaderVisible(true); mGpxWayPointTable.setLinesVisible(true); TableHelper.createTableColumn(mGpxWayPointTable, "Name", SWT.LEFT, "Some Name", PREFS_WAYPOINT_COL_NAME, store); TableHelper.createTableColumn(mGpxWayPointTable, "Longitude", SWT.LEFT, "-199.999999", PREFS_WAYPOINT_COL_LONGITUDE, store); TableHelper.createTableColumn(mGpxWayPointTable, "Latitude", SWT.LEFT, "-199.999999", PREFS_WAYPOINT_COL_LATITUDE, store); TableHelper.createTableColumn(mGpxWayPointTable, "Elevation", SWT.LEFT, "99999.9", PREFS_WAYPOINT_COL_ELEVATION, store); TableHelper.createTableColumn(mGpxWayPointTable, "Description", SWT.LEFT, "Some Description", PREFS_WAYPOINT_COL_DESCRIPTION, store); final TableViewer gpxWayPointViewer = new TableViewer(mGpxWayPointTable); gpxWayPointViewer.setContentProvider(new WayPointContentProvider()); gpxWayPointViewer.setLabelProvider(new WayPointLabelProvider()); gpxWayPointViewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { ISelection selection = event.getSelection(); if (selection instanceof IStructuredSelection) { IStructuredSelection structuredSelection = (IStructuredSelection)selection; Object selectedObject = structuredSelection.getFirstElement(); if (selectedObject instanceof WayPoint) { WayPoint wayPoint = (WayPoint)selectedObject; if (mEmulatorConsole != null && mPlayingTrack == false) { processCommandResult(mEmulatorConsole.sendLocation( wayPoint.getLongitude(), wayPoint.getLatitude(), wayPoint.getElevation())); } } } } }); // table for tracks. mGpxTrackTable = new Table(gpxLocationComp, SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION); mGpxTrackTable.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); gd.heightHint = 100; mGpxTrackTable.setHeaderVisible(true); mGpxTrackTable.setLinesVisible(true); TableHelper.createTableColumn(mGpxTrackTable, "Name", SWT.LEFT, "Some very long name", PREFS_TRACK_COL_NAME, store); TableHelper.createTableColumn(mGpxTrackTable, "Point Count", SWT.RIGHT, "9999", PREFS_TRACK_COL_COUNT, store); TableHelper.createTableColumn(mGpxTrackTable, "First Point Time", SWT.LEFT, "999-99-99T99:99:99Z", PREFS_TRACK_COL_FIRST, store); TableHelper.createTableColumn(mGpxTrackTable, "Last Point Time", SWT.LEFT, "999-99-99T99:99:99Z", PREFS_TRACK_COL_LAST, store); TableHelper.createTableColumn(mGpxTrackTable, "Comment", SWT.LEFT, "-199.999999", PREFS_TRACK_COL_COMMENT, store); final TableViewer gpxTrackViewer = new TableViewer(mGpxTrackTable); gpxTrackViewer.setContentProvider(new TrackContentProvider()); gpxTrackViewer.setLabelProvider(new TrackLabelProvider()); gpxTrackViewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { ISelection selection = event.getSelection(); if (selection instanceof IStructuredSelection) { IStructuredSelection structuredSelection = (IStructuredSelection)selection; Object selectedObject = structuredSelection.getFirstElement(); if (selectedObject instanceof Track) { Track track = (Track)selectedObject; if (mEmulatorConsole != null && mPlayingTrack == false) { TrackPoint[] points = track.getPoints(); processCommandResult(mEmulatorConsole.sendLocation( points[0].getLongitude(), points[0].getLatitude(), points[0].getElevation())); } mPlayGpxButton.setEnabled(true); mGpxBackwardButton.setEnabled(true); mGpxForwardButton.setEnabled(true); mGpxSpeedButton.setEnabled(true); return; } } mPlayGpxButton.setEnabled(false); mGpxBackwardButton.setEnabled(false); mGpxForwardButton.setEnabled(false); mGpxSpeedButton.setEnabled(false); } }); mGpxUploadButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { FileDialog fileDialog = new FileDialog(mParent.getShell(), SWT.OPEN); fileDialog.setText("Load GPX File"); fileDialog.setFilterExtensions(new String[] { "*.gpx" } ); String fileName = fileDialog.open(); if (fileName != null) { GpxParser parser = new GpxParser(fileName); if (parser.parse()) { gpxWayPointViewer.setInput(parser.getWayPoints()); gpxTrackViewer.setInput(parser.getTracks()); } } } }); mGpxPlayControls = new Composite(gpxLocationComp, SWT.NONE); GridLayout gl; mGpxPlayControls.setLayout(gl = new GridLayout(5, false)); gl.marginHeight = gl.marginWidth = 0; mGpxPlayControls.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mPlayGpxButton = new Button(mGpxPlayControls, SWT.PUSH | SWT.FLAT); mPlayGpxButton.setImage(mPlayImage); mPlayGpxButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (mPlayingTrack == false) { ISelection selection = gpxTrackViewer.getSelection(); if (selection.isEmpty() == false && selection instanceof IStructuredSelection) { IStructuredSelection structuredSelection = (IStructuredSelection)selection; Object selectedObject = structuredSelection.getFirstElement(); if (selectedObject instanceof Track) { Track track = (Track)selectedObject; playTrack(track); } } } else { // if we're playing, then we pause mPlayingTrack = false; if (mPlayingThread != null) { mPlayingThread.interrupt(); } } } }); Label separator = new Label(mGpxPlayControls, SWT.SEPARATOR | SWT.VERTICAL); separator.setLayoutData(gd = new GridData( GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL)); gd.heightHint = 0; mGpxBackwardButton = new Button(mGpxPlayControls, SWT.TOGGLE | SWT.FLAT); mGpxBackwardButton.setImage(mImageLoader.loadImage("backward.png", mParent.getDisplay())); // $NON-NLS-1$ mGpxBackwardButton.setSelection(false); mGpxBackwardButton.addSelectionListener(mDirectionButtonAdapter); mGpxForwardButton = new Button(mGpxPlayControls, SWT.TOGGLE | SWT.FLAT); mGpxForwardButton.setImage(mImageLoader.loadImage("forward.png", mParent.getDisplay())); // $NON-NLS-1$ mGpxForwardButton.setSelection(true); mGpxForwardButton.addSelectionListener(mDirectionButtonAdapter); mGpxSpeedButton = new Button(mGpxPlayControls, SWT.PUSH | SWT.FLAT); mSpeedIndex = 0; mSpeed = PLAY_SPEEDS[mSpeedIndex]; mGpxSpeedButton.setText(String.format(SPEED_FORMAT, mSpeed)); mGpxSpeedButton.addSelectionListener(mSpeedButtonAdapter); mPlayGpxButton.setEnabled(false); mGpxBackwardButton.setEnabled(false); mGpxForwardButton.setEnabled(false); mGpxSpeedButton.setEnabled(false); } private void createKmlLocationControl(Composite kmlLocationComp) { GridData gd; IPreferenceStore store = DdmUiPreferences.getStore(); kmlLocationComp.setLayout(new GridLayout(1, false)); mKmlUploadButton = new Button(kmlLocationComp, SWT.PUSH); mKmlUploadButton.setText("Load KML..."); // Table for way point mKmlWayPointTable = new Table(kmlLocationComp, SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION); mKmlWayPointTable.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); gd.heightHint = 200; mKmlWayPointTable.setHeaderVisible(true); mKmlWayPointTable.setLinesVisible(true); TableHelper.createTableColumn(mKmlWayPointTable, "Name", SWT.LEFT, "Some Name", PREFS_WAYPOINT_COL_NAME, store); TableHelper.createTableColumn(mKmlWayPointTable, "Longitude", SWT.LEFT, "-199.999999", PREFS_WAYPOINT_COL_LONGITUDE, store); TableHelper.createTableColumn(mKmlWayPointTable, "Latitude", SWT.LEFT, "-199.999999", PREFS_WAYPOINT_COL_LATITUDE, store); TableHelper.createTableColumn(mKmlWayPointTable, "Elevation", SWT.LEFT, "99999.9", PREFS_WAYPOINT_COL_ELEVATION, store); TableHelper.createTableColumn(mKmlWayPointTable, "Description", SWT.LEFT, "Some Description", PREFS_WAYPOINT_COL_DESCRIPTION, store); final TableViewer kmlWayPointViewer = new TableViewer(mKmlWayPointTable); kmlWayPointViewer.setContentProvider(new WayPointContentProvider()); kmlWayPointViewer.setLabelProvider(new WayPointLabelProvider()); mKmlUploadButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { FileDialog fileDialog = new FileDialog(mParent.getShell(), SWT.OPEN); fileDialog.setText("Load KML File"); fileDialog.setFilterExtensions(new String[] { "*.kml" } ); String fileName = fileDialog.open(); if (fileName != null) { KmlParser parser = new KmlParser(fileName); if (parser.parse()) { kmlWayPointViewer.setInput(parser.getWayPoints()); mPlayKmlButton.setEnabled(true); mKmlBackwardButton.setEnabled(true); mKmlForwardButton.setEnabled(true); mKmlSpeedButton.setEnabled(true); } } } }); kmlWayPointViewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { ISelection selection = event.getSelection(); if (selection instanceof IStructuredSelection) { IStructuredSelection structuredSelection = (IStructuredSelection)selection; Object selectedObject = structuredSelection.getFirstElement(); if (selectedObject instanceof WayPoint) { WayPoint wayPoint = (WayPoint)selectedObject; if (mEmulatorConsole != null && mPlayingTrack == false) { processCommandResult(mEmulatorConsole.sendLocation( wayPoint.getLongitude(), wayPoint.getLatitude(), wayPoint.getElevation())); } } } } }); mKmlPlayControls = new Composite(kmlLocationComp, SWT.NONE); GridLayout gl; mKmlPlayControls.setLayout(gl = new GridLayout(5, false)); gl.marginHeight = gl.marginWidth = 0; mKmlPlayControls.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); mPlayKmlButton = new Button(mKmlPlayControls, SWT.PUSH | SWT.FLAT); mPlayKmlButton.setImage(mPlayImage); mPlayKmlButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (mPlayingTrack == false) { Object input = kmlWayPointViewer.getInput(); if (input instanceof WayPoint[]) { playKml((WayPoint[])input); } } else { // if we're playing, then we pause mPlayingTrack = false; if (mPlayingThread != null) { mPlayingThread.interrupt(); } } } }); Label separator = new Label(mKmlPlayControls, SWT.SEPARATOR | SWT.VERTICAL); separator.setLayoutData(gd = new GridData( GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL)); gd.heightHint = 0; mKmlBackwardButton = new Button(mKmlPlayControls, SWT.TOGGLE | SWT.FLAT); mKmlBackwardButton.setImage(mImageLoader.loadImage("backward.png", mParent.getDisplay())); // $NON-NLS-1$ mKmlBackwardButton.setSelection(false); mKmlBackwardButton.addSelectionListener(mDirectionButtonAdapter); mKmlForwardButton = new Button(mKmlPlayControls, SWT.TOGGLE | SWT.FLAT); mKmlForwardButton.setImage(mImageLoader.loadImage("forward.png", mParent.getDisplay())); // $NON-NLS-1$ mKmlForwardButton.setSelection(true); mKmlForwardButton.addSelectionListener(mDirectionButtonAdapter); mKmlSpeedButton = new Button(mKmlPlayControls, SWT.PUSH | SWT.FLAT); mSpeedIndex = 0; mSpeed = PLAY_SPEEDS[mSpeedIndex]; mKmlSpeedButton.setText(String.format(SPEED_FORMAT, mSpeed)); mKmlSpeedButton.addSelectionListener(mSpeedButtonAdapter); mPlayKmlButton.setEnabled(false); mKmlBackwardButton.setEnabled(false); mKmlForwardButton.setEnabled(false); mKmlSpeedButton.setEnabled(false); } /** * Sets the focus to the proper control inside the panel. */ @Override public void setFocus() { } @Override protected void postCreation() { // pass } private synchronized void setDataMode(int selectionIndex) { if (mEmulatorConsole != null) { processCommandResult(mEmulatorConsole.setGsmDataMode( GsmMode.getEnum(GSM_MODES[selectionIndex][1]))); } } private synchronized void setVoiceMode(int selectionIndex) { if (mEmulatorConsole != null) { processCommandResult(mEmulatorConsole.setGsmVoiceMode( GsmMode.getEnum(GSM_MODES[selectionIndex][1]))); } } private synchronized void setNetworkLatency(int selectionIndex) { if (mEmulatorConsole != null) { processCommandResult(mEmulatorConsole.setNetworkLatency(selectionIndex)); } } private synchronized void setNetworkSpeed(int selectionIndex) { if (mEmulatorConsole != null) { processCommandResult(mEmulatorConsole.setNetworkSpeed(selectionIndex)); } } /** * Callback on device selection change. * @param device the new selected device */ public void handleNewDevice(Device device) { if (mParent.isDisposed()) { return; } // unlink to previous console. synchronized (this) { mEmulatorConsole = null; } try { // get the emulator console for this device // First we need the device itself if (device != null) { GsmStatus gsm = null; NetworkStatus netstatus = null; synchronized (this) { mEmulatorConsole = EmulatorConsole.getConsole(device); if (mEmulatorConsole != null) { // get the gsm status gsm = mEmulatorConsole.getGsmStatus(); netstatus = mEmulatorConsole.getNetworkStatus(); if (gsm == null || netstatus == null) { mEmulatorConsole = null; } } } if (gsm != null && netstatus != null) { Display d = mParent.getDisplay(); if (d.isDisposed() == false) { final GsmStatus f_gsm = gsm; final NetworkStatus f_netstatus = netstatus; d.asyncExec(new Runnable() { public void run() { if (f_gsm.voice != GsmMode.UNKNOWN) { mVoiceMode.select(getGsmComboIndex(f_gsm.voice)); } else { mVoiceMode.clearSelection(); } if (f_gsm.data != GsmMode.UNKNOWN) { mDataMode.select(getGsmComboIndex(f_gsm.data)); } else { mDataMode.clearSelection(); } if (f_netstatus.speed != -1) { mNetworkSpeed.select(f_netstatus.speed); } else { mNetworkSpeed.clearSelection(); } if (f_netstatus.latency != -1) { mNetworkLatency.select(f_netstatus.latency); } else { mNetworkLatency.clearSelection(); } } }); } } } } finally { // enable/disable the ui boolean enable = false; synchronized (this) { enable = mEmulatorConsole != null; } enable(enable); } } /** * Enable or disable the ui. Can be called from non ui threads. * @param enabled */ private void enable(final boolean enabled) { try { Display d = mParent.getDisplay(); d.asyncExec(new Runnable() { public void run() { if (mParent.isDisposed() == false) { doEnable(enabled); } } }); } catch (SWTException e) { // disposed. do nothing } } private boolean isValidPhoneNumber() { String number = mPhoneNumber.getText().trim(); return number.matches(RE_PHONE_NUMBER); } /** * Enable or disable the ui. Cannot be called from non ui threads. * @param enabled */ protected void doEnable(boolean enabled) { mVoiceLabel.setEnabled(enabled); mVoiceMode.setEnabled(enabled); mDataLabel.setEnabled(enabled); mDataMode.setEnabled(enabled); mSpeedLabel.setEnabled(enabled); mNetworkSpeed.setEnabled(enabled); mLatencyLabel.setEnabled(enabled); mNetworkLatency.setEnabled(enabled); // Calling setEnabled on a text field will trigger a modifyText event, so we don't do it // if we don't need to. if (mPhoneNumber.isEnabled() != enabled) { mNumberLabel.setEnabled(enabled); mPhoneNumber.setEnabled(enabled); } boolean valid = isValidPhoneNumber(); mVoiceButton.setEnabled(enabled && valid); mSmsButton.setEnabled(enabled && valid); boolean smsValid = enabled && valid && mSmsButton.getSelection(); // Calling setEnabled on a text field will trigger a modifyText event, so we don't do it // if we don't need to. if (mSmsMessage.isEnabled() != smsValid) { mMessageLabel.setEnabled(smsValid); mSmsMessage.setEnabled(smsValid); } if (enabled == false) { mSmsMessage.setText(""); //$NON-NLs-1$ } mCallButton.setEnabled(enabled && valid); mCancelButton.setEnabled(enabled && valid && mVoiceButton.getSelection()); if (enabled == false) { mVoiceMode.clearSelection(); mDataMode.clearSelection(); mNetworkSpeed.clearSelection(); mNetworkLatency.clearSelection(); if (mPhoneNumber.getText().length() > 0) { mPhoneNumber.setText(""); //$NON-NLS-1$ } } // location controls mLocationFolders.setEnabled(enabled); mDecimalButton.setEnabled(enabled); mSexagesimalButton.setEnabled(enabled); mLongitudeControls.setEnabled(enabled); mLatitudeControls.setEnabled(enabled); mGpxUploadButton.setEnabled(enabled); mGpxWayPointTable.setEnabled(enabled); mGpxTrackTable.setEnabled(enabled); mKmlUploadButton.setEnabled(enabled); mKmlWayPointTable.setEnabled(enabled); } /** * Returns the index of the combo item matching a specific GsmMode. * @param mode */ private int getGsmComboIndex(GsmMode mode) { for (int i = 0 ; i < GSM_MODES.length; i++) { String[] modes = GSM_MODES[i]; if (mode.getTag().equals(modes[1])) { return i; } } return -1; } /** * Processes the result of a command sent to the console. * @param result the result of the command. */ private boolean processCommandResult(final String result) { if (result != EmulatorConsole.RESULT_OK) { try { mParent.getDisplay().asyncExec(new Runnable() { public void run() { if (mParent.isDisposed() == false) { MessageDialog.openError(mParent.getShell(), "Emulator Console", result); } } }); } catch (SWTException e) { // we're quitting, just ignore } return false; } return true; } /** * @param track */ private void playTrack(final Track track) { // no need to synchronize this check, the worst that can happen, is we start the thread // for nothing. if (mEmulatorConsole != null) { mPlayGpxButton.setImage(mPauseImage); mPlayKmlButton.setImage(mPauseImage); mPlayingTrack = true; mPlayingThread = new Thread() { @Override public void run() { try { TrackPoint[] trackPoints = track.getPoints(); int count = trackPoints.length; // get the start index. int start = 0; if (mPlayDirection == -1) { start = count - 1; } for (int p = start; p >= 0 && p < count; p += mPlayDirection) { if (mPlayingTrack == false) { return; } // get the current point and send its location to // the emulator. final TrackPoint trackPoint = trackPoints[p]; synchronized (EmulatorControlPanel.this) { if (mEmulatorConsole == null || processCommandResult(mEmulatorConsole.sendLocation( trackPoint.getLongitude(), trackPoint.getLatitude(), trackPoint.getElevation())) == false) { return; } } // if this is not the final point, then get the next one and // compute the delta time int nextIndex = p + mPlayDirection; if (nextIndex >=0 && nextIndex < count) { TrackPoint nextPoint = trackPoints[nextIndex]; long delta = nextPoint.getTime() - trackPoint.getTime(); if (delta < 0) { delta = -delta; } long startTime = System.currentTimeMillis(); try { sleep(delta / mSpeed); } catch (InterruptedException e) { if (mPlayingTrack == false) { return; } // we got interrupted, lets make sure we can play do { long waited = System.currentTimeMillis() - startTime; long needToWait = delta / mSpeed; if (waited < needToWait) { try { sleep(needToWait - waited); } catch (InterruptedException e1) { // we'll just loop and wait again if needed. // unless we're supposed to stop if (mPlayingTrack == false) { return; } } } else { break; } } while (true); } } } } finally { mPlayingTrack = false; try { mParent.getDisplay().asyncExec(new Runnable() { public void run() { if (mPlayGpxButton.isDisposed() == false) { mPlayGpxButton.setImage(mPlayImage); mPlayKmlButton.setImage(mPlayImage); } } }); } catch (SWTException e) { // we're quitting, just ignore } } } }; mPlayingThread.start(); } } private void playKml(final WayPoint[] trackPoints) { // no need to synchronize this check, the worst that can happen, is we start the thread // for nothing. if (mEmulatorConsole != null) { mPlayGpxButton.setImage(mPauseImage); mPlayKmlButton.setImage(mPauseImage); mPlayingTrack = true; mPlayingThread = new Thread() { @Override public void run() { try { int count = trackPoints.length; // get the start index. int start = 0; if (mPlayDirection == -1) { start = count - 1; } for (int p = start; p >= 0 && p < count; p += mPlayDirection) { if (mPlayingTrack == false) { return; } // get the current point and send its location to // the emulator. WayPoint trackPoint = trackPoints[p]; synchronized (EmulatorControlPanel.this) { if (mEmulatorConsole == null || processCommandResult(mEmulatorConsole.sendLocation( trackPoint.getLongitude(), trackPoint.getLatitude(), trackPoint.getElevation())) == false) { return; } } // if this is not the final point, then get the next one and // compute the delta time int nextIndex = p + mPlayDirection; if (nextIndex >=0 && nextIndex < count) { long delta = 1000; // 1 second if (delta < 0) { delta = -delta; } long startTime = System.currentTimeMillis(); try { sleep(delta / mSpeed); } catch (InterruptedException e) { if (mPlayingTrack == false) { return; } // we got interrupted, lets make sure we can play do { long waited = System.currentTimeMillis() - startTime; long needToWait = delta / mSpeed; if (waited < needToWait) { try { sleep(needToWait - waited); } catch (InterruptedException e1) { // we'll just loop and wait again if needed. // unless we're supposed to stop if (mPlayingTrack == false) { return; } } } else { break; } } while (true); } } } } finally { mPlayingTrack = false; try { mParent.getDisplay().asyncExec(new Runnable() { public void run() { if (mPlayGpxButton.isDisposed() == false) { mPlayGpxButton.setImage(mPlayImage); mPlayKmlButton.setImage(mPlayImage); } } }); } catch (SWTException e) { // we're quitting, just ignore } } } }; mPlayingThread.start(); } } }