aboutsummaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rwxr-xr-xapps/SdkController/SdkControllerApp/AndroidManifest.xml36
-rwxr-xr-xapps/SdkController/SdkControllerApp/project.properties1
-rwxr-xr-xapps/SdkController/SdkControllerApp/res/layout/main.xml2
-rwxr-xr-xapps/SdkController/SdkControllerApp/res/layout/sensor_row.xml23
-rwxr-xr-xapps/SdkController/SdkControllerApp/res/layout/sensors.xml27
-rwxr-xr-xapps/SdkController/SdkControllerApp/res/values/strings.xml13
-rwxr-xr-xapps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/SensorActivity.java34
-rwxr-xr-xapps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/activities/BaseBindingActivity.java111
-rwxr-xr-xapps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/activities/MainActivity.java (renamed from apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/MainActivity.java)124
-rwxr-xr-xapps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/activities/MultitouchActivity.java (renamed from apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/MultitouchActivity.java)2
-rwxr-xr-xapps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/activities/SensorActivity.java232
-rwxr-xr-xapps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/handlers/BaseHandler.java88
-rwxr-xr-xapps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/handlers/MultitouchHandler.java60
-rwxr-xr-xapps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/handlers/SensorsHandler.java607
-rwxr-xr-xapps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/service/ControllerService.java (renamed from apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/ControllerService.java)190
-rwxr-xr-xapps/SdkController/SdkControllerLib/src/com/android/tools/sdkcontroller/lib/EmulatorConnection.java16
16 files changed, 1407 insertions, 159 deletions
diff --git a/apps/SdkController/SdkControllerApp/AndroidManifest.xml b/apps/SdkController/SdkControllerApp/AndroidManifest.xml
index 62e498f..43dcdde 100755
--- a/apps/SdkController/SdkControllerApp/AndroidManifest.xml
+++ b/apps/SdkController/SdkControllerApp/AndroidManifest.xml
@@ -3,24 +3,40 @@
package="com.android.tools.sdkcontroller"
android:versionCode="1"
android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="7"
+ android:targetSdkVersion="15" />
- <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="15"/>
+ <uses-permission android:name="android.permission.INTERNET" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
+
<activity
- android:name=".MainActivity"
- android:label="@string/app_name" android:launchMode="singleInstance">
+ android:name=".activities.MainActivity"
+ android:label="@string/app_name"
+ android:launchMode="singleInstance" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
-
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
- </activity>
- <activity android:name=".SensorActivity" android:launchMode="singleInstance"></activity>
- <activity android:name=".MultitouchActivity" android:launchMode="singleInstance" android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|fontScale" android:screenOrientation="portrait"></activity>
- <service android:icon="@drawable/ic_launcher" android:description="@string/service_description" android:name="ControllerService"></service>
- </application>
+ </activity>
+
+ <activity
+ android:name=".activities.SensorActivity"
+ android:launchMode="singleInstance" />
+
+ <activity
+ android:name=".activities.MultitouchActivity"
+ android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|fontScale"
+ android:launchMode="singleInstance"
+ android:screenOrientation="portrait" />
-</manifest> \ No newline at end of file
+ <service
+ android:name=".service.ControllerService"
+ android:description="@string/service_description"
+ android:icon="@drawable/ic_launcher" />
+ </application>
+</manifest>
diff --git a/apps/SdkController/SdkControllerApp/project.properties b/apps/SdkController/SdkControllerApp/project.properties
index 9c52cb1..3b50fc7 100755
--- a/apps/SdkController/SdkControllerApp/project.properties
+++ b/apps/SdkController/SdkControllerApp/project.properties
@@ -12,3 +12,4 @@
# Project target.
target=android-15
+android.library.reference.1=../SdkControllerLib
diff --git a/apps/SdkController/SdkControllerApp/res/layout/main.xml b/apps/SdkController/SdkControllerApp/res/layout/main.xml
index 612b9fc..3722206 100755
--- a/apps/SdkController/SdkControllerApp/res/layout/main.xml
+++ b/apps/SdkController/SdkControllerApp/res/layout/main.xml
@@ -75,7 +75,7 @@
android:padding="8dp"
android:text="[service errors]"
android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="#FFF0"
+ android:textColor="#FFF0"
tools:ignore="HardcodedText" />
<TextView
diff --git a/apps/SdkController/SdkControllerApp/res/layout/sensor_row.xml b/apps/SdkController/SdkControllerApp/res/layout/sensor_row.xml
new file mode 100755
index 0000000..ca167a7
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/res/layout/sensor_row.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TableRow xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+
+ <CheckBox
+ android:id="@+id/row_checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="10dp"
+ android:saveEnabled="false"
+ android:text="Some CheckBox"
+ tools:ignore="HardcodedText" />
+
+ <TextView
+ android:id="@+id/row_textview"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+</TableRow>
diff --git a/apps/SdkController/SdkControllerApp/res/layout/sensors.xml b/apps/SdkController/SdkControllerApp/res/layout/sensors.xml
new file mode 100755
index 0000000..0fb1e93
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/res/layout/sensors.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/sensors_top_description"
+ />
+
+ <ScrollView
+ android:id="@+id/scrollView1"
+ android:layout_width="fill_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" >
+
+ <TableLayout
+ android:id="@+id/tableLayout"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:saveEnabled="false" />
+
+ </ScrollView>
+
+</LinearLayout> \ No newline at end of file
diff --git a/apps/SdkController/SdkControllerApp/res/values/strings.xml b/apps/SdkController/SdkControllerApp/res/values/strings.xml
index e840166..642d7b8 100755
--- a/apps/SdkController/SdkControllerApp/res/values/strings.xml
+++ b/apps/SdkController/SdkControllerApp/res/values/strings.xml
@@ -20,19 +20,20 @@
<resources>
<!-- Strings for manifest. -->
- <string name="app_name">SdkControllerApp</string>
- <string name="service_description">Background service for SdkController</string>
+ <string name="app_name">Sdk Controller App</string>
+ <string name="service_description">Background service for Sdk Controller App</string>
<!-- Strings for service. -->
<string name="service_notif_title">SdkController is running</string>
-
+
<!-- Strings for layout/main -->
<string name="main_text_intro">(insert a description of the purpose of this app here)</string>
<string name="main_label_service">Service:</string>
<string name="main_label_buttons">What you can do:</string>
<string name="main_btn_open_multitouch">Control Multi-touch</string>
<string name="main_btn_open_sensors">Control Sensors</string>
- <string name="main_service_status_running">Running</string>
- <string name="main_service_status_stopped">Stopped</string>
-
+ <string name="main_service_status_connected">Emulator Connected</string>
+ <string name="main_service_status_disconnected">Emulator Connected</string>
+ <string name="sensors_top_description">Available Sensors:</string>
+
</resources> \ No newline at end of file
diff --git a/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/SensorActivity.java b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/SensorActivity.java
deleted file mode 100755
index 64edb01..0000000
--- a/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/SensorActivity.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.tools.sdkcontroller;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class SensorActivity extends Activity {
-
- public static String TAG = SensorActivity.class.getSimpleName();
- @SuppressWarnings("unused")
- private static boolean DEBUG = true;
-
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- //TODO setContentView(R.layout.sensors);
- }
-}
diff --git a/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/activities/BaseBindingActivity.java b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/activities/BaseBindingActivity.java
new file mode 100755
index 0000000..c94da1e
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/activities/BaseBindingActivity.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.tools.sdkcontroller.activities;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.tools.sdkcontroller.service.ControllerService;
+import com.android.tools.sdkcontroller.service.ControllerService.ControllerBinder;
+import com.android.tools.sdkcontroller.service.ControllerService.ControllerListener;
+
+/**
+ * Base activity class that knows how to bind and unbind from the
+ * {@link ControllerService}.
+ */
+public abstract class BaseBindingActivity extends Activity {
+
+ public static String TAG = BaseBindingActivity.class.getSimpleName();
+ private static boolean DEBUG = true;
+ private ServiceConnection mServiceConnection;
+ private ControllerBinder mServiceBinder;
+
+ public ControllerBinder getServiceBinder() {
+ return mServiceBinder;
+ }
+
+ @Override
+ protected void onResume() {
+ if (DEBUG) Log.d(TAG, "onResume");
+ super.onResume();
+ bindToService();
+ }
+
+ @Override
+ protected void onPause() {
+ if (DEBUG) Log.d(TAG, "onPause");
+ super.onPause();
+ unbindFromService();
+ }
+
+ // ----------
+
+ protected abstract ControllerListener createControllerListener();
+ protected abstract void onServiceConnected();
+ protected abstract void onServiceDisconnected();
+
+ /**
+ * Starts the service and binds to it.
+ */
+ protected void bindToService() {
+ if (mServiceConnection == null) {
+ final ControllerListener listener = createControllerListener();
+
+ mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (DEBUG) Log.d(TAG, "Activity connected to service");
+ mServiceBinder = (ControllerBinder) service;
+ mServiceBinder.addListener(listener);
+ BaseBindingActivity.this.onServiceConnected();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG) Log.d(TAG, "Activity disconnected from service");
+ mServiceBinder = null;
+ BaseBindingActivity.this.onServiceDisconnected();
+ }
+ };
+ }
+
+ // Start service so that it doesn't stop when we unbind
+ if (DEBUG) Log.d(TAG, "start requested & bind service");
+ Intent service = new Intent(this, ControllerService.class);
+ startService(service);
+ bindService(service,
+ mServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ }
+
+ /**
+ * Unbinds from the service but does not actually stop the service.
+ * This lets us have it run in the background even if this isn't the active app.
+ */
+ protected void unbindFromService() {
+ if (mServiceConnection != null) {
+ if (DEBUG) Log.d(TAG, "unbind service");
+ unbindService(mServiceConnection);
+ mServiceConnection = null;
+ }
+ }
+} \ No newline at end of file
diff --git a/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/MainActivity.java b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/activities/MainActivity.java
index fb3d223..b221e4f 100755
--- a/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/MainActivity.java
+++ b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/activities/MainActivity.java
@@ -14,15 +14,10 @@
* the License.
*/
-package com.android.tools.sdkcontroller;
+package com.android.tools.sdkcontroller.activities;
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
-import android.content.ServiceConnection;
import android.os.Bundle;
-import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
@@ -32,18 +27,19 @@ import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.TextView;
import android.widget.ToggleButton;
-import com.android.tools.sdkcontroller.ControllerService.ControllerBinder;
-import com.android.tools.sdkcontroller.ControllerService.ControllerListener;
+import com.android.tools.sdkcontroller.R;
+import com.android.tools.sdkcontroller.service.ControllerService;
+import com.android.tools.sdkcontroller.service.ControllerService.ControllerBinder;
+import com.android.tools.sdkcontroller.service.ControllerService.ControllerListener;
-public class MainActivity extends Activity {
+public class MainActivity extends BaseBindingActivity {
+ @SuppressWarnings("hiding")
public static String TAG = MainActivity.class.getSimpleName();
private static boolean DEBUG = true;
private Button mBtnOpenMultitouch;
private Button mBtnOpenSensors;
private ToggleButton mBtnToggleService;
- private ServiceConnection mServiceConnection;
- protected ControllerBinder mServiceBinder;
private TextView mTextError;
private TextView mTextStatus;
@@ -61,34 +57,46 @@ public class MainActivity extends Activity {
@Override
protected void onResume() {
- if (DEBUG) Log.d(TAG, "onResume");
+ // BaseBindingActivity.onResume will bind to the service.
super.onResume();
- bindToService();
updateError();
}
@Override
protected void onPause() {
- if (DEBUG) Log.d(TAG, "onPause");
+ // BaseBindingActivity.onResume will unbind from (but not stop) the service.
super.onPause();
- // On pause we unbind but don't stop -- this is the case when the users goes home
- // or invokes any other activity, including our owns.
- boolean isRunning = mServiceBinder != null;
- unbindFromService();
}
@Override
public void onBackPressed() {
if (DEBUG) Log.d(TAG, "onBackPressed");
- // If back is pressed, we stop the service automatically. It seems more intuitive that way.
+ // If back is pressed, we stop the service automatically.
+ // It seems more intuitive that way.
stopService();
super.onBackPressed();
}
// ----------
- private void setupButtons() {
+ @Override
+ protected void onServiceConnected() {
+ updateButtons();
+ }
+
+ @Override
+ protected void onServiceDisconnected() {
+ updateButtons();
+ }
+
+ @Override
+ protected ControllerListener createControllerListener() {
+ return new MainControllerListener();
+ }
+ // ----------
+
+ private void setupButtons() {
mBtnOpenMultitouch = (Button) findViewById(R.id.btnOpenMultitouch);
mBtnOpenSensors = (Button) findViewById(R.id.btnOpenSensors);
@@ -133,56 +141,6 @@ public class MainActivity extends Activity {
mBtnOpenMultitouch.setEnabled(running);
mBtnOpenSensors.setEnabled(running);
mBtnToggleService.setChecked(running);
-
- mTextStatus.setText(
- getText(running ? R.string.main_service_status_running
- : R.string.main_service_status_stopped));
- }
-
- /**
- * Starts the service and binds to it.
- */
- private void bindToService() {
- if (mServiceConnection == null) {
- final ControllerListener listener = new OurControllerListener();
-
- mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (DEBUG) Log.d(TAG, "Activity connected to service");
- mServiceBinder = (ControllerBinder) service;
- mServiceBinder.addListener(listener);
- updateButtons();
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- if (DEBUG) Log.d(TAG, "Activity disconnected from service");
- mServiceBinder = null;
- updateButtons();
- }
- };
- }
-
- // Start service so that it doesn't stop when we unbind
- if (DEBUG) Log.d(TAG, "start requested & bind service");
- Intent service = new Intent(this, ControllerService.class);
- startService(service);
- bindService(service,
- mServiceConnection,
- Context.BIND_AUTO_CREATE);
- }
-
- /**
- * Unbinds from the service but does not actually stop the service.
- * This lets us have it run in the background even if this isn't the active app.
- */
- private void unbindFromService() {
- if (mServiceConnection != null) {
- if (DEBUG) Log.d(TAG, "unbind service");
- unbindService(mServiceConnection);
- mServiceConnection = null;
- }
}
/**
@@ -195,9 +153,9 @@ public class MainActivity extends Activity {
stopService(service);
}
- private class OurControllerListener implements ControllerListener {
+ private class MainControllerListener implements ControllerListener {
@Override
- public void onErrorChanged(String error) {
+ public void onErrorChanged() {
runOnUiThread(new Runnable() {
@Override
public void run() {
@@ -205,10 +163,21 @@ public class MainActivity extends Activity {
}
});
}
+
+ @Override
+ public void onStatusChanged() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ updateStatus();
+ }
+ });
+ }
}
private void updateError() {
- String error = mServiceBinder == null ? "" : mServiceBinder.getSensorErrors();
+ ControllerBinder binder = getServiceBinder();
+ String error = binder == null ? "" : binder.getSensorErrors();
if (error == null) {
error = "";
}
@@ -216,4 +185,13 @@ public class MainActivity extends Activity {
mTextError.setVisibility(error.length() == 0 ? View.GONE : View.VISIBLE);
mTextError.setText(error);
}
+
+ private void updateStatus() {
+ ControllerBinder binder = getServiceBinder();
+ boolean connected = binder == null ? false : binder.isEmuConnected();
+ mTextStatus.setText(
+ getText(connected ? R.string.main_service_status_connected
+ : R.string.main_service_status_disconnected));
+
+ }
} \ No newline at end of file
diff --git a/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/MultitouchActivity.java b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/activities/MultitouchActivity.java
index fd9bf22..0e65482 100755
--- a/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/MultitouchActivity.java
+++ b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/activities/MultitouchActivity.java
@@ -14,7 +14,7 @@
* the License.
*/
-package com.android.tools.sdkcontroller;
+package com.android.tools.sdkcontroller.activities;
import android.app.Activity;
import android.os.Bundle;
diff --git a/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/activities/SensorActivity.java b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/activities/SensorActivity.java
new file mode 100755
index 0000000..14a1c72
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/activities/SensorActivity.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.tools.sdkcontroller.activities;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+import android.widget.TextView;
+
+import com.android.tools.sdkcontroller.R;
+import com.android.tools.sdkcontroller.handlers.BaseHandler.HandlerType;
+import com.android.tools.sdkcontroller.handlers.BaseHandler.UiListener;
+import com.android.tools.sdkcontroller.handlers.SensorsHandler;
+import com.android.tools.sdkcontroller.handlers.SensorsHandler.MonitoredSensor;
+import com.android.tools.sdkcontroller.service.ControllerService.ControllerBinder;
+import com.android.tools.sdkcontroller.service.ControllerService.ControllerListener;
+
+public class SensorActivity extends BaseBindingActivity {
+
+ @SuppressWarnings("hiding")
+ public static String TAG = SensorActivity.class.getSimpleName();
+ @SuppressWarnings("unused")
+ private static boolean DEBUG = true;
+
+ private TableLayout mTableLayout;
+ private SensorsHandler mSensorHandler;
+ private final OurUiListener mUiListener = new OurUiListener();
+ private final Map<MonitoredSensor, DisplayInfo> mDisplayedSensors =
+ new HashMap<SensorsHandler.MonitoredSensor, SensorActivity.DisplayInfo>();
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.sensors);
+ mTableLayout = (TableLayout) findViewById(R.id.tableLayout);
+
+ }
+
+ @Override
+ protected void onResume() {
+ // BaseBindingActivity.onResume will bind to the service.
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ // BaseBindingActivity.onResume will unbind from (but not stop) the service.
+ super.onPause();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ removeSensorUi();
+ }
+
+ // ----------
+
+ @Override
+ protected void onServiceConnected() {
+ createSensorUi();
+ }
+
+ @Override
+ protected void onServiceDisconnected() {
+ removeSensorUi();
+ }
+
+ @Override
+ protected ControllerListener createControllerListener() {
+ return new SensorsControllerListener();
+ }
+
+ // ----------
+
+ private class SensorsControllerListener implements ControllerListener {
+ @Override
+ public void onErrorChanged() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ //--updateError();
+ }
+ });
+ }
+
+ @Override
+ public void onStatusChanged() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ ControllerBinder binder = getServiceBinder();
+ mTableLayout.setEnabled(binder.isEmuConnected());
+ }
+ });
+ }
+ }
+
+ private void createSensorUi() {
+ final LayoutInflater inflater = getLayoutInflater();
+
+ if (!mDisplayedSensors.isEmpty()) {
+ removeSensorUi();
+ }
+
+ mSensorHandler = (SensorsHandler) getServiceBinder().getHandler(HandlerType.Sensor);
+ if (mSensorHandler != null) {
+ mSensorHandler.addUiListener(mUiListener);
+
+ assert mDisplayedSensors.isEmpty();
+ List<MonitoredSensor> sensors = mSensorHandler.getSensors();
+ for (MonitoredSensor sensor : sensors) {
+ final TableRow row = (TableRow) inflater.inflate(R.layout.sensor_row, mTableLayout, false);
+ mTableLayout.addView(row);
+ mDisplayedSensors.put(sensor, new DisplayInfo(sensor, row));
+ }
+ }
+ }
+
+ private void removeSensorUi() {
+ mTableLayout.removeAllViews();
+ mSensorHandler.removeUiListener(mUiListener);
+ mSensorHandler = null;
+ for (DisplayInfo info : mDisplayedSensors.values()) {
+ info.release();
+ }
+ mDisplayedSensors.clear();
+ }
+
+ private class DisplayInfo implements CompoundButton.OnCheckedChangeListener {
+ private MonitoredSensor mSensor;
+ private CheckBox mChk;
+ private TextView mVal;
+
+ public DisplayInfo(MonitoredSensor sensor, TableRow row) {
+ mSensor = sensor;
+
+ // Initialize displayed checkbox for this sensor, and register
+ // checked state listener for it.
+ mChk = (CheckBox) row.findViewById(R.id.row_checkbox);
+ mChk.setText(sensor.getUiName());
+ mChk.setEnabled(sensor.isEnabledByEmulator());
+ mChk.setChecked(sensor.isEnabledByUser());
+ mChk.setOnCheckedChangeListener(this);
+
+ // Initialize displayed text box for this sensor.
+ mVal = (TextView) row.findViewById(R.id.row_textview);
+ mVal.setText(sensor.getValue());
+ }
+
+ /**
+ * Handles checked state change for the associated CheckBox. If check
+ * box is checked we will register sensor change listener. If it is
+ * unchecked, we will unregister sensor change listener.
+ */
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (mSensor != null) {
+ mSensor.onCheckedChanged(isChecked);
+ }
+ }
+
+ public void release() {
+ mChk = null;
+ mVal = null;
+ mSensor = null;
+
+ }
+
+ public void updateState() {
+ if (mChk != null && mSensor != null) {
+ mChk.setEnabled(mSensor.isEnabledByEmulator());
+ mChk.setChecked(mSensor.isEnabledByUser());
+ }
+ }
+
+ public void updateValue() {
+ if (mVal != null && mSensor != null) {
+ mVal.setText(mSensor.getValue());
+ }
+ }
+ }
+
+ private class OurUiListener implements UiListener {
+ @Override
+ public void onHandlerEvent(final int event, final Object... params) {
+ // This is invoked from the emulator connection thread.
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ DisplayInfo info = null;
+ switch(event) {
+ case SensorsHandler.SENSOR_STATE_CHANGED:
+ info = mDisplayedSensors.get(params[0]);
+ if (info != null) {
+ info.updateState();
+ }
+ break;
+ case SensorsHandler.SENSOR_DISPLAY_MODIFIED:
+ info = mDisplayedSensors.get(params[0]);
+ if (info != null) {
+ info.updateValue();
+ }
+ break;
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/handlers/BaseHandler.java b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/handlers/BaseHandler.java
new file mode 100755
index 0000000..2db0f43
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/handlers/BaseHandler.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.tools.sdkcontroller.handlers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.Context;
+
+import com.android.tools.sdkcontroller.lib.EmulatorConnection;
+import com.android.tools.sdkcontroller.lib.EmulatorListener;
+
+
+
+public abstract class BaseHandler {
+
+ public enum HandlerType {
+ MultiTouch,
+ Sensor
+ }
+
+ public abstract HandlerType getType();
+
+ public abstract int getPort();
+
+ public abstract void onStart(EmulatorConnection connection, Context context);
+
+ public abstract void onStop();
+
+ // ------------
+ // Interaction from the emulator connection towards the handler
+
+ /**
+ * Uses the first non-null replies to this query.
+ * @see EmulatorListener#onEmulatorQuery(String, String)
+ */
+ public abstract String onEmulatorQuery(String query, String param);
+
+ /**
+ * Uses the first non-null replies to this query.
+ * @see EmulatorListener#onEmulatorBlobQuery(byte[])
+ */
+ public abstract String onEmulatorBlobQuery(byte[] array);
+
+ // ------------
+ // Interaction from handler towards listening UI
+
+ public interface UiListener {
+ public void onHandlerEvent(int event, Object...params);
+ }
+
+ private final List<UiListener> mUiListeners = new ArrayList<UiListener>();
+
+ public void addUiListener(UiListener listener) {
+ assert listener != null;
+ if (listener != null) {
+ if (!mUiListeners.contains(listener)) {
+ mUiListeners.add(listener);
+ }
+ }
+ }
+
+ public void removeUiListener(UiListener listener) {
+ assert listener != null;
+ mUiListeners.remove(listener);
+ }
+
+ protected void notifyUi(int event, Object...params) {
+ for (UiListener listener : mUiListeners) {
+ listener.onHandlerEvent(event, params);
+ }
+ }
+
+}
diff --git a/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/handlers/MultitouchHandler.java b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/handlers/MultitouchHandler.java
new file mode 100755
index 0000000..a6032c9
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/handlers/MultitouchHandler.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.tools.sdkcontroller.handlers;
+
+import android.content.Context;
+
+import com.android.tools.sdkcontroller.lib.EmulatorConnection;
+
+
+public class MultitouchHandler extends BaseHandler {
+
+ @Override
+ public HandlerType getType() {
+ return HandlerType.MultiTouch;
+ }
+
+ @Override
+ public int getPort() {
+ return EmulatorConnection.MULTITOUCH_PORT;
+ }
+
+ @Override
+ public void onStart(EmulatorConnection connection, Context context) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onStop() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public String onEmulatorQuery(String query, String param) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String onEmulatorBlobQuery(byte[] array) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/handlers/SensorsHandler.java b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/handlers/SensorsHandler.java
new file mode 100755
index 0000000..f6c3068
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/handlers/SensorsHandler.java
@@ -0,0 +1,607 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.tools.sdkcontroller.handlers;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingDeque;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.util.Log;
+
+import com.android.tools.sdkcontroller.lib.EmulatorConnection;
+
+
+public class SensorsHandler extends BaseHandler {
+
+ private static String TAG = SensorsHandler.class.getSimpleName();
+ private static boolean DEBUG = true;
+
+ /**
+ * Sensor "enabled by emulator" state has changed.
+ * Parameters are [0]=MonitoredSensor, [1]=Boolean isEnabledByEmulator.
+ */
+ public static final int SENSOR_STATE_CHANGED = 1;
+ /**
+ * Sensor display value has changed.
+ * Parameters are [0]=MonitoredSensor, [1]=String value.
+ */
+ public static final int SENSOR_DISPLAY_MODIFIED = 2;
+
+ /** Array containing monitored sensors. */
+ private final List<MonitoredSensor> mSensors = new ArrayList<MonitoredSensor>();
+ private EmulatorConnection mConnection;
+ private SensorManager mSenMan;
+
+ public SensorsHandler() {
+ }
+
+ @Override
+ public HandlerType getType() {
+ return HandlerType.Sensor;
+ }
+
+ @Override
+ public int getPort() {
+ return EmulatorConnection.SENSORS_PORT;
+ }
+
+ public List<MonitoredSensor> getSensors() {
+ return mSensors;
+ }
+
+ @Override
+ public void onStart(EmulatorConnection connection, Context context) {
+ mConnection = connection;
+ mNotificationThread.start();
+
+ // Iterate through the available sensors, adding them to the array.
+ SensorManager sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ mSenMan = sm;
+ List<Sensor> sensors = sm.getSensorList(Sensor.TYPE_ALL);
+ int cur_index = 0;
+ for (int n = 0; n < sensors.size(); n++) {
+ Sensor avail_sensor = sensors.get(n);
+
+ // There can be multiple sensors of the same type. We need only one.
+ if (!isSensorTypeAlreadyMonitored(avail_sensor.getType())) {
+ // The first sensor we've got for the given type is not
+ // necessarily the right one. So, use the default sensor
+ // for the given type.
+ Sensor def_sens = sm.getDefaultSensor(avail_sensor.getType());
+ MonitoredSensor to_add = new MonitoredSensor(def_sens);
+ cur_index++;
+ mSensors.add(to_add);
+ if (DEBUG) Log.d(TAG, String.format(
+ "Monitoring sensor #%02d: Name = '%s', Type = 0x%x",
+ cur_index, def_sens.getName(), def_sens.getType()));
+ }
+ }
+ }
+
+ @Override
+ public void onStop() {
+ stopSensors();
+ // To stop the notification queue, we nullify the connect and send a msg
+ mConnection = null;
+ mNotificationQueue.offer("<end>");
+ }
+
+ /**
+ * Called when a query is received from the emulator. NOTE: This method is
+ * called from the I/O loop.
+ *
+ * @param query Name of the query received from the emulator. The allowed
+ * queries are: 'list' - Lists sensors that are monitored by this
+ * application. The application replies to this command with a
+ * string: 'List:<name1>\n<name2>\n...<nameN>\n\0" 'start' -
+ * Starts monitoring sensors. There is no reply for this command.
+ * 'stop' - Stops monitoring sensors. There is no reply for this
+ * command. 'enable:<sensor|all> - Enables notifications for a
+ * sensor / all sensors. 'disable:<sensor|all> - Disables
+ * notifications for a sensor / all sensors.
+ * @param param Query parameters.
+ * @return Zero-terminated reply string. String must be formatted as such:
+ * "ok|ko[:reply data]"
+ */
+ @Override
+ public String onEmulatorQuery(String query, String param) {
+ if (query.contentEquals("list")) {
+ return onQueryList();
+ } else if (query.contentEquals("start")) {
+ return onQueryStart();
+ } else if (query.contentEquals("stop")) {
+ return onQueryStop();
+ } else if (query.contentEquals("enable")) {
+ return onQueryEnable(param);
+ } else if (query.contentEquals("disable")) {
+ return onQueryDisable(param);
+ } else {
+ Log.e(TAG, "Unknown query " + query + "(" + param + ")");
+ return "ko:Query is unknown\0";
+ }
+ }
+
+ /**
+ * Called when a BLOB query is received from the emulator. NOTE: This method
+ * is called from the I/O loop, so all communication with the emulator will
+ * be "on hold" until this method returns.
+ *
+ * @param array contains BLOB data for the query.
+ * @return Zero-terminated reply string. String must be formatted as such:
+ * "ok|ko[:reply data]"
+ */
+ @Override
+ public String onEmulatorBlobQuery(byte[] array) {
+ return "ko:Unexpected\0";
+ }
+
+ /***************************************************************************
+ * Query handlers
+ **************************************************************************/
+
+ /**
+ * Handles 'list' query.
+ *
+ * @return List of emulator-friendly names for sensors that are available on
+ * the device.
+ */
+ private String onQueryList() {
+ // List monitored sensors.
+ String list = "ok:";
+ for (MonitoredSensor sensor : mSensors) {
+ list += sensor.getEmulatorFriendlyName();
+ list += "\n";
+ }
+ list += '\0'; // Response must end with zero-terminator.
+ return list;
+ }
+
+ /**
+ * Handles 'start' query.
+ *
+ * @return Empty string. This is a "command" query that doesn't assume any
+ * response.
+ */
+ private String onQueryStart() {
+ startSensors();
+ return "ok\0";
+ }
+
+ /**
+ * Handles 'stop' query.
+ *
+ * @return Empty string. This is a "command" query that doesn't assume any
+ * response.
+ */
+ private String onQueryStop() {
+ stopSensors();
+ return "ok\0";
+ }
+
+ /**
+ * Handles 'enable' query.
+ *
+ * @param param Sensor selector: - all Enables all available sensors, or -
+ * <name> Emulator-friendly name of a sensor to enable.
+ * @return "ok" / "ko": success / failure.
+ */
+ private String onQueryEnable(String param) {
+ if (param.contentEquals("all")) {
+ // Enable all sensors.
+ for (MonitoredSensor sensor : mSensors) {
+ sensor.enableSensor();
+ }
+ return "ok\0";
+ }
+
+ // Lookup sensor by emulator-friendly name.
+ MonitoredSensor sensor = getSensorByEFN(param);
+ if (sensor != null) {
+ sensor.enableSensor();
+ return "ok\0";
+ } else {
+ return "ko:Sensor not found\0";
+ }
+ }
+
+ /**
+ * Handles 'disable' query.
+ *
+ * @param param Sensor selector: - all Disables all available sensors, or -
+ * <name> Emulator-friendly name of a sensor to disable.
+ * @return "ok" / "ko": success / failure.
+ */
+ private String onQueryDisable(String param) {
+ if (param.contentEquals("all")) {
+ // Disable all sensors.
+ for (MonitoredSensor sensor : mSensors) {
+ sensor.disableSensor();
+ }
+ return "ok\0";
+ }
+
+ // Lookup sensor by emulator-friendly name.
+ MonitoredSensor sensor = getSensorByEFN(param);
+ if (sensor != null) {
+ sensor.disableSensor();
+ return "ok\0";
+ } else {
+ return "ko:Sensor not found\0";
+ }
+ }
+
+ /***************************************************************************
+ * Internals
+ **************************************************************************/
+
+ private final BlockingQueue<String> mNotificationQueue = new LinkedBlockingDeque<String>();
+ private final Thread mNotificationThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while(true) {
+ try {
+ String msg = mNotificationQueue.take();
+ if (mConnection == null) {
+ return;
+ }
+ mConnection.sendNotification(msg);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "NotifQueue.take", e);
+ }
+ }
+ }
+ }, "sensorNotif");
+
+ /**
+ * Sends sensor's event to the emulator.
+ *
+ * @param msg Sensor's event message.
+ */
+ private void sendSensorEvent(String msg) {
+ try {
+ mNotificationQueue.put(msg);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "NotifQueue.put", e);
+ }
+ }
+
+ /**
+ * Start listening to all monitored sensors.
+ */
+ private void startSensors() {
+ for (MonitoredSensor sensor : mSensors) {
+ sensor.startListening();
+ }
+ }
+
+ /**
+ * Stop listening to all monitored sensors.
+ */
+ private void stopSensors() {
+ for (MonitoredSensor sensor : mSensors) {
+ sensor.stopListening();
+ }
+ }
+
+ /**
+ * Checks if a sensor for the given type is already monitored.
+ *
+ * @param type Sensor type (one of the Sensor.TYPE_XXX constants)
+ * @return true if a sensor for the given type is already monitored, or
+ * false if the sensor is not monitored.
+ */
+ private boolean isSensorTypeAlreadyMonitored(int type) {
+ for (MonitoredSensor sensor : mSensors) {
+ if (sensor.getType() == type) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Looks up a monitored sensor by its emulator-friendly name.
+ *
+ * @param name Emulator-friendly name to look up the monitored sensor for.
+ * @return Monitored sensor for the fiven name, or null if sensor was not
+ * found.
+ */
+ private MonitoredSensor getSensorByEFN(String name) {
+ for (MonitoredSensor sensor : mSensors) {
+ if (sensor.mEmulatorFriendlyName.contentEquals(name)) {
+ return sensor;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Encapsulates a sensor that is being monitored. To monitor sensor changes
+ * each monitored sensor registers with sensor manager as a sensor listener.
+ * To control sensor monitoring from the UI, each monitored sensor has two
+ * UI controls associated with it: - A check box (named after sensor) that
+ * can be used to enable, or disable listening to the sensor changes. - A
+ * text view where current sensor value is displayed.
+ */
+ public class MonitoredSensor {
+ /** Sensor to monitor. */
+ private final Sensor mSensor;
+ /** The sensor name to display in the UI. */
+ private String mUiName = "";
+ /** Text view displaying the value of the sensor. */
+ private String mValue = "";
+ /** Emulator-friendly name for the sensor. */
+ private String mEmulatorFriendlyName;
+ /** Formats string to show in the TextView. */
+ private String mTextFmt;
+ /** Formats string to send to the emulator. */
+ private String mMsgFmt;
+ /**
+ * Enabled state. This state is controlled by the emulator, that
+ * maintains its own list of sensors. So, if a sensor is missing, or is
+ * disabled in the emulator, it should be disabled in this application.
+ */
+ private boolean mEnabledByEmulator = false;
+ /** User-controlled enabled state. */
+ private boolean mEnabledByUser = true;
+ private final OurSensorEventListener mListener = new OurSensorEventListener();
+
+ /**
+ * Constructs MonitoredSensor instance, and register the listeners.
+ *
+ * @param sensor Sensor to monitor.
+ */
+ MonitoredSensor(Sensor sensor) {
+ mSensor = sensor;
+ mEnabledByUser = true;
+
+ // Set appropriate sensor name depending on the type. Unfortunately,
+ // we can't really use sensor.getName() here, since the value it
+ // returns (although resembles the purpose) is a bit vaguer than it
+ // should be. Also choose an appropriate format for the strings that
+ // display sensor's value, and strings that are sent to the
+ // emulator.
+ switch (sensor.getType()) {
+ case Sensor.TYPE_ACCELEROMETER:
+ mUiName = "Accelerometer";
+ // 3 floats.
+ mTextFmt = "%+.2f %+.2f %+.2f";
+ mEmulatorFriendlyName = "acceleration";
+ mMsgFmt = mEmulatorFriendlyName + ":%g:%g:%g\0";
+ break;
+ case 9: // Sensor.TYPE_GRAVITY is missing in API 7
+ // 3 floats.
+ mUiName = "Gravity";
+ mTextFmt = "%+.2f %+.2f %+.2f";
+ mEmulatorFriendlyName = "gravity";
+ mMsgFmt = mEmulatorFriendlyName + ":%g:%g:%g\0";
+ break;
+ case Sensor.TYPE_GYROSCOPE:
+ mUiName = "Gyroscope";
+ // 3 floats.
+ mTextFmt = "%+.2f %+.2f %+.2f";
+ mEmulatorFriendlyName = "gyroscope";
+ mMsgFmt = mEmulatorFriendlyName + ":%g:%g:%g\0";
+ break;
+ case Sensor.TYPE_LIGHT:
+ mUiName = "Light";
+ // 1 integer.
+ mTextFmt = "%.0f";
+ mEmulatorFriendlyName = "light";
+ mMsgFmt = mEmulatorFriendlyName + ":%g\0";
+ break;
+ case 10: // Sensor.TYPE_LINEAR_ACCELERATION is missing in API 7
+ mUiName = "Linear acceleration";
+ // 3 floats.
+ mTextFmt = "%+.2f %+.2f %+.2f";
+ mEmulatorFriendlyName = "linear-acceleration";
+ mMsgFmt = mEmulatorFriendlyName + ":%g:%g:%g\0";
+ break;
+ case Sensor.TYPE_MAGNETIC_FIELD:
+ mUiName = "Magnetic field";
+ // 3 floats.
+ mTextFmt = "%+.2f %+.2f %+.2f";
+ mEmulatorFriendlyName = "magnetic-field";
+ mMsgFmt = mEmulatorFriendlyName + ":%g:%g:%g\0";
+ break;
+ case Sensor.TYPE_ORIENTATION:
+ mUiName = "Orientation";
+ // 3 integers.
+ mTextFmt = "%+03.0f %+03.0f %+03.0f";
+ mEmulatorFriendlyName = "orientation";
+ mMsgFmt = mEmulatorFriendlyName + ":%g:%g:%g\0";
+ break;
+ case Sensor.TYPE_PRESSURE:
+ mUiName = "Pressure";
+ // 1 integer.
+ mTextFmt = "%.0f";
+ mEmulatorFriendlyName = "pressure";
+ mMsgFmt = mEmulatorFriendlyName + ":%g\0";
+ break;
+ case Sensor.TYPE_PROXIMITY:
+ mUiName = "Proximity";
+ // 1 integer.
+ mTextFmt = "%.0f";
+ mEmulatorFriendlyName = "proximity";
+ mMsgFmt = mEmulatorFriendlyName + ":%g\0";
+ break;
+ case 11: // Sensor.TYPE_ROTATION_VECTOR is missing in API 7
+ mUiName = "Rotation";
+ // 3 floats.
+ mTextFmt = "%+.2f %+.2f %+.2f";
+ mEmulatorFriendlyName = "rotation";
+ mMsgFmt = mEmulatorFriendlyName + ":%g:%g:%g\0";
+ break;
+ case Sensor.TYPE_TEMPERATURE:
+ mUiName = "Temperature";
+ // 1 integer.
+ mTextFmt = "%.0f";
+ mEmulatorFriendlyName = "tempterature";
+ mMsgFmt = mEmulatorFriendlyName + ":%g\0";
+ break;
+ default:
+ mUiName = "<Unknown>";
+ mTextFmt = "N/A";
+ mEmulatorFriendlyName = "unknown";
+ mMsgFmt = mEmulatorFriendlyName + "\0";
+ if (DEBUG) Log.e(TAG, "Unknown sensor type " + mSensor.getType() + " for sensor "
+ + mSensor.getName());
+ break;
+ }
+ }
+
+ public String getUiName() {
+ return mUiName;
+ }
+
+ public String getValue() {
+ return mValue;
+ }
+
+ public boolean isEnabledByEmulator() {
+ return mEnabledByEmulator;
+ }
+
+ public boolean isEnabledByUser() {
+ return mEnabledByUser;
+ }
+
+ /**
+ * Handles checked state change for the associated CheckBox. If check
+ * box is checked we will register sensor change listener. If it is
+ * unchecked, we will unregister sensor change listener.
+ */
+ public void onCheckedChanged(boolean isChecked) {
+ mEnabledByUser = isChecked;
+ if (isChecked) {
+ startListening();
+ } else {
+ stopListening();
+ }
+ }
+
+ // ---------
+
+ /**
+ * Gets sensor type.
+ *
+ * @return Sensor type as one of the Sensor.TYPE_XXX constants.
+ */
+ private int getType() {
+ return mSensor.getType();
+ }
+
+ /**
+ * Gets sensor's emulator-friendly name.
+ *
+ * @return Sensor's emulator-friendly name.
+ */
+ private String getEmulatorFriendlyName() {
+ return mEmulatorFriendlyName;
+ }
+
+ /**
+ * Starts monitoring the sensor. NOTE: This method is called from
+ * outside of the UI thread.
+ */
+ private void startListening() {
+ if (mEnabledByEmulator && mEnabledByUser) {
+ if (DEBUG) Log.d(TAG, "+++ Sensor " + getEmulatorFriendlyName() + " is started.");
+ mSenMan.registerListener(mListener, mSensor, SensorManager.SENSOR_DELAY_UI);
+ }
+ }
+
+ /**
+ * Stops monitoring the sensor. NOTE: This method is called from outside
+ * of the UI thread.
+ */
+ private void stopListening() {
+ if (DEBUG) Log.d(TAG, "--- Sensor " + getEmulatorFriendlyName() + " is stopped.");
+ mSenMan.unregisterListener(mListener);
+ }
+
+ /**
+ * Enables sensor events. NOTE: This method is called from outside of
+ * the UI thread.
+ */
+ private void enableSensor() {
+ if (DEBUG) Log.d(TAG, ">>> Sensor " + getEmulatorFriendlyName() + " is enabled.");
+ mEnabledByEmulator = true;
+ mValue = "";
+ notifyUi(SENSOR_STATE_CHANGED, this, mEnabledByEmulator);
+ }
+
+ /**
+ * Disables sensor events. NOTE: This method is called from outside of
+ * the UI thread.
+ */
+ private void disableSensor() {
+ Log.e(TAG, "<<< Sensor " + getEmulatorFriendlyName() + " is disabled.");
+ mEnabledByEmulator = false;
+ mValue = "Disabled by emulator";
+ notifyUi(SENSOR_STATE_CHANGED, this, mEnabledByEmulator);
+ }
+
+ private class OurSensorEventListener implements SensorEventListener {
+ /**
+ * Handles "sensor changed" event. This is an implementation of the
+ * SensorEventListener interface.
+ */
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ // Display current sensor value, and format message that will be
+ // sent to the emulator.
+ final int nArgs = event.values.length;
+ String msg;
+ String val;
+ if (nArgs == 3) {
+ val = String.format(mTextFmt, event.values[0], event.values[1], event.values[2]);
+ msg = String.format(mMsgFmt, event.values[0], event.values[1], event.values[2]);
+ } else if (nArgs == 2) {
+ val = String.format(mTextFmt, event.values[0], event.values[1]);
+ msg = String.format(mMsgFmt, event.values[0], event.values[1]);
+ } else if (nArgs == 1) {
+ val = String.format(mTextFmt, event.values[0]);
+ msg = String.format(mMsgFmt, event.values[0]);
+ } else {
+ Log.e(TAG, "Unexpected number of values " + event.values.length
+ + " in onSensorChanged for sensor " + mSensor.getName());
+ return;
+ }
+ mValue = val;
+ sendSensorEvent(msg);
+ notifyUi(SENSOR_DISPLAY_MODIFIED, this, val);
+ }
+
+ /**
+ * Handles "sensor accuracy changed" event. This is an implementation of
+ * the SensorEventListener interface.
+ */
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ }
+ } // MonitoredSensor
+
+}
diff --git a/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/ControllerService.java b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/service/ControllerService.java
index b67ac45..3c2ab2f 100755
--- a/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/ControllerService.java
+++ b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/service/ControllerService.java
@@ -14,10 +14,13 @@
* the License.
*/
-package com.android.tools.sdkcontroller;
+package com.android.tools.sdkcontroller.service;
+import java.io.IOException;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import android.app.Activity;
import android.app.Notification;
@@ -27,9 +30,18 @@ import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
-import android.os.SystemClock;
import android.util.Log;
+import com.android.tools.sdkcontroller.R;
+import com.android.tools.sdkcontroller.activities.MainActivity;
+import com.android.tools.sdkcontroller.handlers.BaseHandler;
+import com.android.tools.sdkcontroller.handlers.BaseHandler.HandlerType;
+import com.android.tools.sdkcontroller.handlers.MultitouchHandler;
+import com.android.tools.sdkcontroller.handlers.SensorsHandler;
+import com.android.tools.sdkcontroller.lib.EmulatorConnection;
+import com.android.tools.sdkcontroller.lib.EmulatorConnection.EmulatorConnectionType;
+import com.android.tools.sdkcontroller.lib.EmulatorListener;
+
/**
* The background service of the SdkController.
* There can be only one instance of this.
@@ -58,6 +70,8 @@ public class ControllerService extends Service {
/** Internal error reported by the service. */
private String mSensorError = "";
+ private final Set<EmuCnxHandler> mHandlers = new HashSet<ControllerService.EmuCnxHandler>();
+
/**
* Interface that the service uses to notify binded activities.
* <p/>
@@ -69,9 +83,13 @@ public class ControllerService extends Service {
/**
* The error string reported by the service has changed. <br/>
* Note this may be called from a thread different than the UI thread.
- * @param error The new error string.
*/
- void onErrorChanged(String error);
+ void onErrorChanged();
+
+ /**
+ * The service status has changed (emulator connected/disconnected.)
+ */
+ void onStatusChanged();
}
/** Interface that callers can use to access the service. */
@@ -83,6 +101,7 @@ public class ControllerService extends Service {
* @param listener A non-null listener. Ignored if already listed.
*/
public void addListener(ControllerListener listener) {
+ assert listener != null;
if (listener != null) {
synchronized(mListeners) {
if (!mListeners.contains(listener)) {
@@ -98,6 +117,7 @@ public class ControllerService extends Service {
* @param listener A listener to remove. Can be null.
*/
public void removeListener(ControllerListener listener) {
+ assert listener != null;
synchronized(mListeners) {
mListeners.remove(listener);
}
@@ -106,6 +126,25 @@ public class ControllerService extends Service {
public String getSensorErrors() {
return mSensorError;
}
+
+ public boolean isEmuConnected() {
+ for (EmuCnxHandler handler : mHandlers) {
+ if (!handler.isConnected()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public BaseHandler getHandler(HandlerType type) {
+ for (EmuCnxHandler handler : mHandlers) {
+ BaseHandler h = handler.getHandler();
+ if (h.getType() == type) {
+ return h;
+ }
+ }
+ return null;
+ }
}
/**
@@ -151,32 +190,131 @@ public class ControllerService extends Service {
// ------
/**
- * Called when the service has been created.
+ * Wrapper that associates one {@link EmulatorConnection} with
+ * one {@link BaseHandler}. Ideally we would not need this if all
+ * the action handlers were using the same port, so this wrapper
+ * is just temporary.
*/
- private void onServiceStarted() {
- // TODO: add stuff to do when the service starts (e.g. activate sensors?)
-
- // Hack: just do see if this is working, change the error field for a little while.
- Thread t = new Thread(new Runnable() {
- @Override
- public void run() {
- for (int i = 1; i <= 5 && gServiceIsRunning; i++) {
- SystemClock.sleep(1000); // 1s
- resetError();
- addError("Test msg from service thread " + i);
+ private class EmuCnxHandler implements EmulatorListener {
+
+ private EmulatorConnection mCnx;
+ private boolean mConnected;
+ private final BaseHandler mHandler;
+
+ public EmuCnxHandler(BaseHandler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public void onEmulatorConnected() {
+ mConnected = true;
+ notifyStatusChanged();
+ }
+
+ @Override
+ public void onEmulatorDisconnected() {
+ mConnected = false;
+ notifyStatusChanged();
+ }
+
+ @Override
+ public String onEmulatorQuery(String query, String param) {
+ return mHandler.onEmulatorQuery(query, param);
+ }
+
+ @Override
+ public String onEmulatorBlobQuery(byte[] array) {
+ return mHandler.onEmulatorBlobQuery(array);
+ }
+
+ EmuCnxHandler connect() throws InterruptedException {
+ assert mCnx == null;
+
+ final EmuCnxHandler cnxHandler = this;
+
+ // Apps targeting Honeycomb SDK can't do network IO on their main UI
+ // thread. So just start the connection from a thread and then join it.
+ // Yes there's some irony in there.
+ Thread t = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mCnx = new EmulatorConnection(mHandler.getPort(),
+ EmulatorConnectionType.SYNC_CONNECTION,
+ cnxHandler);
+ } catch (IOException e) {
+ addError("Connection failed: " + e.toString());
+ Log.e(TAG, "EmuConnection failed");
+ }
}
- resetError();
+ }, "EmuCnxH.connect");
+ t.start();
+ t.join();
+
+ mHandler.onStart(mCnx, ControllerService.this /*context*/);
+ return this;
+ }
+
+ void disconnect() {
+ if (mCnx != null) {
+ mHandler.onStop();
+ mCnx.disconnect();
+ mCnx = null;
}
- });
- t.start();
+ }
+
+ boolean isConnected() {
+ return mConnected;
+ }
+
+ public BaseHandler getHandler() {
+ return mHandler;
+ }
+ }
+
+ private void disconnectAll() {
+ for(EmuCnxHandler handler : mHandlers) {
+ handler.disconnect();
+ }
+ mHandlers.clear();
+ }
+
+ /**
+ * Called when the service has been created.
+ */
+ private void onServiceStarted() {
+ try {
+ disconnectAll();
+
+ assert mHandlers.isEmpty();
+ mHandlers.add(new EmuCnxHandler(new MultitouchHandler()).connect());
+ mHandlers.add(new EmuCnxHandler(new SensorsHandler()).connect());
+ } catch (Exception e) {
+ addError("Connection failed: " + e.toString());
+ }
}
/**
* Called when the service is being destroyed.
*/
private void onServiceStopped() {
+ disconnectAll();
+ }
+
+ private void notifyErrorChanged() {
+ synchronized(mListeners) {
+ for (ControllerListener listener : mListeners) {
+ listener.onErrorChanged();
+ }
+ }
+ }
- // TODO: add stuff to do when the service stops (e.g. release sensors?)
+ private void notifyStatusChanged() {
+ synchronized(mListeners) {
+ for (ControllerListener listener : mListeners) {
+ listener.onStatusChanged();
+ }
+ }
}
/**
@@ -185,11 +323,7 @@ public class ControllerService extends Service {
private void resetError() {
mSensorError = "";
- synchronized(mListeners) {
- for (ControllerListener listener : mListeners) {
- listener.onErrorChanged(mSensorError);
- }
- }
+ notifyErrorChanged();
}
/**
@@ -203,11 +337,7 @@ public class ControllerService extends Service {
}
mSensorError += error;
- synchronized(mListeners) {
- for (ControllerListener listener : mListeners) {
- listener.onErrorChanged(mSensorError);
- }
- }
+ notifyErrorChanged();
}
/**
diff --git a/apps/SdkController/SdkControllerLib/src/com/android/tools/sdkcontroller/lib/EmulatorConnection.java b/apps/SdkController/SdkControllerLib/src/com/android/tools/sdkcontroller/lib/EmulatorConnection.java
index 8bf1fb2..aa21ca7 100755
--- a/apps/SdkController/SdkControllerLib/src/com/android/tools/sdkcontroller/lib/EmulatorConnection.java
+++ b/apps/SdkController/SdkControllerLib/src/com/android/tools/sdkcontroller/lib/EmulatorConnection.java
@@ -221,7 +221,7 @@ public class EmulatorConnection {
public void run() {
theReader();
}
- }).start();
+ }, "EmuSyncChannel").start();
}
/***********************************************************************
@@ -549,13 +549,16 @@ public class EmulatorConnection {
/**
* Constructs EmulatorConnection instance.
+ * <p/>
+ * Important: Apps targeting Honeycomb+ SDK are not allowed to do networking on their main
+ * thread. The caller is responsible to make sure this is NOT called from a main UI thread.
*
* @param port TCP port where emulator connects.
* @param ctype Defines connection type to use (sync / async). See comments
* to EmulatorConnection class for more info.
* @throws IOException
*/
- private void constructEmulator(int port, EmulatorConnectionType ctype) throws IOException {
+ private void constructEmulator(final int port, EmulatorConnectionType ctype) throws IOException {
mConnectionType = ctype;
// Create I/O looper.
mSelector = SelectorProvider.provider().openSelector();
@@ -565,12 +568,14 @@ public class EmulatorConnection {
mServerSocket = ServerSocketChannel.open();
mServerSocket.configureBlocking(false);
InetAddress local = InetAddress.getLocalHost();
- InetSocketAddress address = new InetSocketAddress(local, port);
+ final InetSocketAddress address = new InetSocketAddress(local, port);
mServerSocket.socket().bind(address);
// Register 'accept' I/O on the server socket.
mServerSocket.register(mSelector, SelectionKey.OP_ACCEPT);
+ // TODO we need a call back onBindSuccess(success or exception)
+
Logv("EmulatorConnection listener is created for port " + port);
// Start I/O looper and dispatcher.
new Thread(new Runnable() {
@@ -578,11 +583,14 @@ public class EmulatorConnection {
public void run() {
runIOLooper();
}
- }).start();
+ }, "EmuCnxIoLoop").start();
}
/**
* Sends a notification message to the emulator via 'event' channel.
+ * <p/>
+ * Important: Apps targeting Honeycomb+ SDK are not allowed to do networking on their main
+ * thread. The caller is responsible to make sure this is NOT called from a main UI thread.
*
* @param msg
*/