blob: 4410d3a3dca8d2725c8797f15106e9c60b3f30fc [file] [log] [blame]
/*
* 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.IDevice;
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(IDevice 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();
}
}
}