Skeleton for a SdkController app with a sevice.

This is just a basic app that has a background
service and an activity to start/stop the service.
The service has a binder interface (so that the
activities can control the service) and a listener
(so that the service can report to activities.)

The service automatically starts with the app and
there's a toggle for the user to turn it off/on.

The activity also reports errors from the service, if any.

There's a notification when the service is running.
which brings back to the control activity.

Change-Id: I67d37e3d905eb328f4d1f7d13fb118f4c7077b74
diff --git a/apps/SdkController/.gitignore b/apps/SdkController/.gitignore
deleted file mode 100644
index d3a6cac..0000000
--- a/apps/SdkController/.gitignore
+++ /dev/null
@@ -1,9 +0,0 @@
-bin
-gen
-.classpath
-.project
-SdkControllerLib/bin/
-SdkControllerSensor/bin/
-SdkControllerSensor/gen/
-SdkControllerMultitouch/bin/
-SdkControllerMultitouch/gen/
diff --git a/apps/SdkController/SdkControllerApp/.classpath b/apps/SdkController/SdkControllerApp/.classpath
new file mode 100755
index 0000000..a4f1e40
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<classpath>

+	<classpathentry kind="src" path="src"/>

+	<classpathentry kind="src" path="gen"/>

+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>

+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>

+	<classpathentry kind="output" path="bin/classes"/>

+</classpath>

diff --git a/apps/SdkController/SdkControllerApp/.project b/apps/SdkController/SdkControllerApp/.project
new file mode 100755
index 0000000..a2d5c18
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>SdkControllerApp</name>

+	<comment></comment>

+	<projects>

+	</projects>

+	<buildSpec>

+		<buildCommand>

+			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.jdt.core.javabuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>com.android.ide.eclipse.adt.ApkBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+	</natures>

+</projectDescription>

diff --git a/apps/SdkController/SdkControllerApp/AndroidManifest.xml b/apps/SdkController/SdkControllerApp/AndroidManifest.xml
new file mode 100755
index 0000000..62e498f
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>

+<manifest xmlns:android="http://schemas.android.com/apk/res/android"

+    package="com.android.tools.sdkcontroller"

+    android:versionCode="1"

+    android:versionName="1.0" >

+

+    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="15"/>

+

+    <application

+        android:icon="@drawable/ic_launcher"

+        android:label="@string/app_name" >

+        <activity

+            android:name=".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>

+

+</manifest>
\ No newline at end of file
diff --git a/apps/SdkController/SdkControllerApp/proguard-project.txt b/apps/SdkController/SdkControllerApp/proguard-project.txt
new file mode 100755
index 0000000..f2fe155
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
diff --git a/apps/SdkController/SdkControllerApp/project.properties b/apps/SdkController/SdkControllerApp/project.properties
new file mode 100755
index 0000000..9c52cb1
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}\tools\proguard\proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-15
diff --git a/apps/SdkController/SdkControllerApp/res/drawable-hdpi/ic_launcher.png b/apps/SdkController/SdkControllerApp/res/drawable-hdpi/ic_launcher.png
new file mode 100755
index 0000000..96a442e
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/apps/SdkController/SdkControllerApp/res/drawable-ldpi/ic_launcher.png b/apps/SdkController/SdkControllerApp/res/drawable-ldpi/ic_launcher.png
new file mode 100755
index 0000000..9923872
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/apps/SdkController/SdkControllerApp/res/drawable-mdpi/ic_launcher.png b/apps/SdkController/SdkControllerApp/res/drawable-mdpi/ic_launcher.png
new file mode 100755
index 0000000..359047d
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/apps/SdkController/SdkControllerApp/res/drawable-xhdpi/ic_launcher.png b/apps/SdkController/SdkControllerApp/res/drawable-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..71c6d76
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/apps/SdkController/SdkControllerApp/res/layout/main.xml b/apps/SdkController/SdkControllerApp/res/layout/main.xml
new file mode 100755
index 0000000..612b9fc
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/res/layout/main.xml
Binary files differ
diff --git a/apps/SdkController/SdkControllerApp/res/values/strings.xml b/apps/SdkController/SdkControllerApp/res/values/strings.xml
new file mode 100755
index 0000000..e840166
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/res/values/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>

+<!--

+/*

+ * 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.

+ */

+-->

+

+<resources>

+

+    <!-- Strings for manifest. -->

+    <string name="app_name">SdkControllerApp</string>

+    <string name="service_description">Background service for SdkController</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>

+    

+</resources>
\ No newline at end of file
diff --git a/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/ControllerService.java b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/ControllerService.java
new file mode 100755
index 0000000..b67ac45
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/ControllerService.java
@@ -0,0 +1,246 @@
+/*

+ * 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 java.util.ArrayList;

+import java.util.List;

+

+import android.app.Activity;

+import android.app.Notification;

+import android.app.NotificationManager;

+import android.app.PendingIntent;

+import android.app.Service;

+import android.content.Intent;

+import android.os.Binder;

+import android.os.IBinder;

+import android.os.SystemClock;

+import android.util.Log;

+

+/**

+ * The background service of the SdkController.

+ * There can be only one instance of this.

+ */

+public class ControllerService extends Service {

+

+    /*

+     * Implementation reference:

+     * http://developer.android.com/reference/android/app/Service.html#LocalServiceSample

+     */

+

+    public static String TAG = ControllerService.class.getSimpleName();

+    private static boolean DEBUG = true;

+

+    private static int NOTIF_ID = 'S' << 24 + 'd' << 16 + 'k' << 8 + 'C' << 0;

+

+    private final IBinder mBinder = new ControllerBinder();

+

+    private List<ControllerListener> mListeners = new ArrayList<ControllerListener>();

+

+    /**

+     * Whether the service is running. Set to true in onCreate, false in onDestroy.

+     */

+    private static volatile boolean gServiceIsRunning = false;

+

+    /** Internal error reported by the service. */

+    private String mSensorError = "";

+

+    /**

+     * Interface that the service uses to notify binded activities.

+     * <p/>

+     * As a design rule, implementations of this listener should be aware that most calls

+     * will NOT happen on the UI thread. Any access to the UI should be properly protected

+     * by using {@link Activity#runOnUiThread(Runnable)}.

+     */

+    public interface ControllerListener {

+        /**

+         * 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);

+    }

+

+    /** Interface that callers can use to access the service. */

+    public class ControllerBinder extends Binder {

+

+        /**

+         * Adds a new listener that will be notified when the service state changes.

+         *

+         * @param listener A non-null listener. Ignored if already listed.

+         */

+        public void addListener(ControllerListener listener) {

+            if (listener != null) {

+                synchronized(mListeners) {

+                    if (!mListeners.contains(listener)) {

+                        mListeners.add(listener);

+                    }

+                }

+            }

+        }

+

+        /**

+         * Removes a listener.

+         *

+         * @param listener A listener to remove. Can be null.

+         */

+        public void removeListener(ControllerListener listener) {

+            synchronized(mListeners) {

+                mListeners.remove(listener);

+            }

+        }

+

+        public String getSensorErrors() {

+            return mSensorError;

+        }

+    }

+

+    /**

+     * Whether the service is running. Set to true in onCreate, false in onDestroy.

+     */

+    public static boolean isServiceIsRunning() {

+        return gServiceIsRunning;

+    }

+

+    @Override

+    public void onCreate() {

+        super.onCreate();

+        if (DEBUG) Log.d(TAG, "Service onCreate");

+        gServiceIsRunning = true;

+        showNotification();

+        onServiceStarted();

+    }

+

+    @Override

+    public int onStartCommand(Intent intent, int flags, int startId) {

+        // We want this service to continue running until it is explicitly

+        // stopped, so return sticky.

+        if (DEBUG) Log.d(TAG, "Service onStartCommand");

+        return START_STICKY;

+    }

+

+    @Override

+    public IBinder onBind(Intent intent) {

+        if (DEBUG) Log.d(TAG, "Service onBind");

+        return mBinder;

+    }

+

+    @Override

+    public void onDestroy() {

+        if (DEBUG) Log.d(TAG, "Service onDestroy");

+        gServiceIsRunning = false;

+        removeNotification();

+        resetError();

+        onServiceStopped();

+        super.onDestroy();

+    }

+

+    // ------

+

+    /**

+     * Called when the service has been created.

+     */

+    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);

+                }

+                resetError();

+            }

+        });

+        t.start();

+    }

+

+    /**

+     * Called when the service is being destroyed.

+     */

+    private void onServiceStopped() {

+

+        // TODO: add stuff to do when the service stops (e.g. release sensors?)

+    }

+

+    /**

+     * Resets the error string and notify listeners.

+     */

+    private void resetError() {

+        mSensorError = "";

+

+        synchronized(mListeners) {

+            for (ControllerListener listener : mListeners) {

+                listener.onErrorChanged(mSensorError);

+            }

+        }

+    }

+

+    /**

+     * An internal utility method to add a line to the error string and notify listeners.

+     * @param error A non-null non-empty error line. \n will be added automatically.

+     */

+    private void addError(String error) {

+        Log.e(TAG, error);

+        if (mSensorError.length() > 0) {

+            mSensorError += "\n";

+        }

+        mSensorError += error;

+

+        synchronized(mListeners) {

+            for (ControllerListener listener : mListeners) {

+                listener.onErrorChanged(mSensorError);

+            }

+        }

+    }

+

+    /**

+     * Displays a notification showing that the service is running.

+     * When the user touches the notification, it opens the main activity

+     * which allows the user to stop this service.

+     */

+    @SuppressWarnings("deprecated")

+    private void showNotification() {

+        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

+

+        String text = getString(R.string.service_notif_title);

+

+        // Note: Notification is marked as deprecated -- in API 11+ there's a new Builder class

+        // but we need to have API 7 compatibility so we ignore that warning.

+

+        Notification n = new Notification(R.drawable.ic_launcher, text, System.currentTimeMillis());

+        n.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;

+        Intent intent = new Intent(this, MainActivity.class);

+        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

+        PendingIntent pi = PendingIntent.getActivity(

+                this,     //context

+                0,        //requestCode

+                intent,   //intent

+                0         // pending intent flags

+                );

+        n.setLatestEventInfo(this, text, text, pi);

+

+        nm.notify(NOTIF_ID, n);

+    }

+

+    private void removeNotification() {

+        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

+        nm.cancel(NOTIF_ID);

+    }

+}

diff --git a/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/MainActivity.java b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/MainActivity.java
new file mode 100755
index 0000000..fb3d223
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/MainActivity.java
@@ -0,0 +1,219 @@
+/*
+ * 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.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;
+import android.widget.Button;
+import android.widget.CompoundButton;
+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;
+
+public class MainActivity extends Activity {
+
+    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;
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        mTextError  = (TextView) findViewById(R.id.textError);
+        mTextStatus = (TextView) findViewById(R.id.textStatus);
+
+        setupButtons();
+    }
+
+    @Override
+    protected void onResume() {
+        if (DEBUG) Log.d(TAG, "onResume");
+        super.onResume();
+        bindToService();
+        updateError();
+    }
+
+    @Override
+    protected void onPause() {
+        if (DEBUG) Log.d(TAG, "onPause");
+        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.
+        stopService();
+        super.onBackPressed();
+    }
+
+    // ----------
+
+    private void setupButtons() {
+
+        mBtnOpenMultitouch = (Button) findViewById(R.id.btnOpenMultitouch);
+        mBtnOpenSensors    = (Button) findViewById(R.id.btnOpenSensors);
+
+        mBtnOpenMultitouch.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                // Open the multi-touch activity.
+                Intent i = new Intent(MainActivity.this, MultitouchActivity.class);
+                startActivity(i);
+            }
+        });
+
+        mBtnOpenSensors.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                // Open the sensor activity.
+                Intent i = new Intent(MainActivity.this, SensorActivity.class);
+                startActivity(i);
+            }
+        });
+
+        mBtnToggleService = (ToggleButton) findViewById(R.id.toggleService);
+
+        // set initial state
+        updateButtons();
+
+        mBtnToggleService.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if (isChecked) {
+                    bindToService();
+                } else {
+                    stopService();
+                }
+            }
+        });
+
+    }
+
+    private void updateButtons() {
+        boolean running = ControllerService.isServiceIsRunning();
+        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;
+        }
+    }
+
+    /**
+     * Unbind and then actually stops the service.
+     */
+    private void stopService() {
+        Intent service = new Intent(this, ControllerService.class);
+        unbindFromService();
+        if (DEBUG) Log.d(TAG, "stop service requested");
+        stopService(service);
+    }
+
+    private class OurControllerListener implements ControllerListener {
+        @Override
+        public void onErrorChanged(String error) {
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    updateError();
+                }
+            });
+        }
+    }
+
+    private void updateError() {
+        String error = mServiceBinder == null ? "" : mServiceBinder.getSensorErrors();
+        if (error == null) {
+            error = "";
+        }
+
+        mTextError.setVisibility(error.length() == 0 ? View.GONE : View.VISIBLE);
+        mTextError.setText(error);
+    }
+}
\ 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/MultitouchActivity.java
new file mode 100755
index 0000000..fd9bf22
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/MultitouchActivity.java
@@ -0,0 +1,34 @@
+/*

+ * 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 MultitouchActivity extends Activity {

+

+    public static String TAG = MultitouchActivity.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.multitouch);

+    }

+}

diff --git a/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/SensorActivity.java b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/SensorActivity.java
new file mode 100755
index 0000000..64edb01
--- /dev/null
+++ b/apps/SdkController/SdkControllerApp/src/com/android/tools/sdkcontroller/SensorActivity.java
@@ -0,0 +1,34 @@
+/*

+ * 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/SdkControllerLib/.classpath b/apps/SdkController/SdkControllerLib/.classpath
new file mode 100755
index 0000000..a4f1e40
--- /dev/null
+++ b/apps/SdkController/SdkControllerLib/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<classpath>

+	<classpathentry kind="src" path="src"/>

+	<classpathentry kind="src" path="gen"/>

+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>

+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>

+	<classpathentry kind="output" path="bin/classes"/>

+</classpath>

diff --git a/apps/SdkController/SdkControllerLib/.project b/apps/SdkController/SdkControllerLib/.project
new file mode 100755
index 0000000..efa52a5
--- /dev/null
+++ b/apps/SdkController/SdkControllerLib/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>SdkControllerLib</name>

+	<comment></comment>

+	<projects>

+	</projects>

+	<buildSpec>

+		<buildCommand>

+			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.jdt.core.javabuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>com.android.ide.eclipse.adt.ApkBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+	</natures>

+</projectDescription>

diff --git a/apps/SdkController/SdkControllerLib/project.properties b/apps/SdkController/SdkControllerLib/project.properties
index 337e8f3..5fa344c 100755
--- a/apps/SdkController/SdkControllerLib/project.properties
+++ b/apps/SdkController/SdkControllerLib/project.properties
@@ -8,5 +8,5 @@
 # project structure.
 
 # Project target.
-target=android-7
+target=android-15
 android.library=true
diff --git a/apps/SdkController/SdkControllerMultitouch/.classpath b/apps/SdkController/SdkControllerMultitouch/.classpath
new file mode 100755
index 0000000..a4f1e40
--- /dev/null
+++ b/apps/SdkController/SdkControllerMultitouch/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<classpath>

+	<classpathentry kind="src" path="src"/>

+	<classpathentry kind="src" path="gen"/>

+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>

+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>

+	<classpathentry kind="output" path="bin/classes"/>

+</classpath>

diff --git a/apps/SdkController/SdkControllerMultitouch/.project b/apps/SdkController/SdkControllerMultitouch/.project
new file mode 100755
index 0000000..7498997
--- /dev/null
+++ b/apps/SdkController/SdkControllerMultitouch/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>SdkControllerMultitouch</name>

+	<comment></comment>

+	<projects>

+	</projects>

+	<buildSpec>

+		<buildCommand>

+			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.jdt.core.javabuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>com.android.ide.eclipse.adt.ApkBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+	</natures>

+</projectDescription>

diff --git a/apps/SdkController/SdkControllerMultitouch/project.properties b/apps/SdkController/SdkControllerMultitouch/project.properties
index d3d8dc4..1234e7b 100644
--- a/apps/SdkController/SdkControllerMultitouch/project.properties
+++ b/apps/SdkController/SdkControllerMultitouch/project.properties
@@ -8,5 +8,5 @@
 # project structure.
 
 # Project target.
-target=android-7
+target=android-15
 android.library.reference.1=../SdkControllerLib
diff --git a/apps/SdkController/SdkControllerSensor/.classpath b/apps/SdkController/SdkControllerSensor/.classpath
new file mode 100755
index 0000000..a4f1e40
--- /dev/null
+++ b/apps/SdkController/SdkControllerSensor/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<classpath>

+	<classpathentry kind="src" path="src"/>

+	<classpathentry kind="src" path="gen"/>

+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>

+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>

+	<classpathentry kind="output" path="bin/classes"/>

+</classpath>

diff --git a/apps/SdkController/SdkControllerSensor/.project b/apps/SdkController/SdkControllerSensor/.project
new file mode 100755
index 0000000..4a5bf31
--- /dev/null
+++ b/apps/SdkController/SdkControllerSensor/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>SdkControllerSensor</name>

+	<comment></comment>

+	<projects>

+	</projects>

+	<buildSpec>

+		<buildCommand>

+			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.jdt.core.javabuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>com.android.ide.eclipse.adt.ApkBuilder</name>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>

+		<nature>org.eclipse.jdt.core.javanature</nature>

+	</natures>

+</projectDescription>

diff --git a/apps/SdkController/SdkControllerSensor/project.properties b/apps/SdkController/SdkControllerSensor/project.properties
index d3d8dc4..1234e7b 100755
--- a/apps/SdkController/SdkControllerSensor/project.properties
+++ b/apps/SdkController/SdkControllerSensor/project.properties
@@ -8,5 +8,5 @@
 # project structure.
 
 # Project target.
-target=android-7
+target=android-15
 android.library.reference.1=../SdkControllerLib