auto import from //depot/cupcake/@137055
diff --git a/apps/CustomLocale/Android.mk b/apps/CustomLocale/Android.mk
new file mode 100644
index 0000000..275207f
--- /dev/null
+++ b/apps/CustomLocale/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := user
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := CustomLocale
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/apps/CustomLocale/AndroidManifest.xml b/apps/CustomLocale/AndroidManifest.xml
new file mode 100644
index 0000000..9dfa896
--- /dev/null
+++ b/apps/CustomLocale/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:versionCode="1"
+    android:versionName="1.0" package="com.android.customlocale">
+    <application
+        android:icon="@drawable/icon"
+        android:label="@string/app_name">
+        <activity
+            android:label="@string/app_name" android:name="CustomLocaleActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:name="NewLocaleDialog"
+            android:theme="@android:style/Theme.Dialog" />
+    </application>
+    <uses-sdk android:minSdkVersion="3" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
+</manifest>
diff --git a/apps/CustomLocale/res/drawable/icon.png b/apps/CustomLocale/res/drawable/icon.png
new file mode 100644
index 0000000..cb40a19
--- /dev/null
+++ b/apps/CustomLocale/res/drawable/icon.png
Binary files differ
diff --git a/apps/CustomLocale/res/layout/list_item.xml b/apps/CustomLocale/res/layout/list_item.xml
new file mode 100644
index 0000000..8c59f92
--- /dev/null
+++ b/apps/CustomLocale/res/layout/list_item.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:padding="5dip">
+    <TextView
+        android:id="@+id/locale_code"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:textAppearanceLarge"
+        android:layout_weight="1"
+        android:text="@string/locale_default" />
+    <TextView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/locale_name"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:textAppearance"
+        android:layout_weight="1" />
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CustomLocale/res/layout/main.xml b/apps/CustomLocale/res/layout/main.xml
new file mode 100644
index 0000000..b1eaa51
--- /dev/null
+++ b/apps/CustomLocale/res/layout/main.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+    <TextView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="0"
+        android:text="@string/header_current_locale"
+        android:textAppearance="@style/TextAppearance.header"
+        android:gravity="center_horizontal"
+        android:background="@color/header_background" />
+    <TextView
+        android:id="@+id/current_locale"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="0"
+        android:padding="5dip" />
+    <TextView
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="0"
+        android:text="@string/header_locale_list"
+        android:textAppearance="@style/TextAppearance.header"
+        android:gravity="center_horizontal"
+        android:background="@color/header_background" />
+    <ListView
+        android:id="@id/android:list"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:layout_weight="1"
+        android:padding="8dip" />
+    <TextView
+        android:id="@id/android:empty"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:text="@string/no_data_label" />
+    <Button
+        android:id="@+id/new_locale"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="0"
+        android:paddingLeft="8dip"
+        android:paddingRight="8dip"
+        android:text="@string/add_new_locale_button" />
+</LinearLayout>
diff --git a/apps/CustomLocale/res/layout/new_locale.xml b/apps/CustomLocale/res/layout/new_locale.xml
new file mode 100644
index 0000000..d0d9d6c
--- /dev/null
+++ b/apps/CustomLocale/res/layout/new_locale.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:paddingLeft="8dip"
+    android:paddingRight="8dip">
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/new_locale_label" />
+    <EditText
+        android:id="@+id/value"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:hint="@string/locale_default" android:inputType="text"/>
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content">
+        <Button
+            android:id="@+id/add"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/add_button" android:layout_gravity="center_vertical"/>
+        <Button
+            android:id="@+id/add_and_select"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/add_select_button" android:layout_gravity="center_vertical"/>
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CustomLocale/res/values-fr/strings.xml b/apps/CustomLocale/res/values-fr/strings.xml
new file mode 100644
index 0000000..8852a9a
--- /dev/null
+++ b/apps/CustomLocale/res/values-fr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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>
+    <string name="app_name">Locales Personalisées</string>
+    <string name="add_new_locale_button">Ajouter une nouvelle locale</string>
+    <string name="new_locale_label">Code de la nouvelle locale:</string>
+    <string name="add_button">Ajouter</string>
+    <string name="no_data_label">Aucune locale</string>
+    <string name="header_current_locale">Locale courrante</string>
+    <string name="header_locale_list">Liste des locales</string>
+    <string name="add_select_button">Ajouter et sélectionner</string>
+</resources>
diff --git a/apps/CustomLocale/res/values/colors.xml b/apps/CustomLocale/res/values/colors.xml
new file mode 100644
index 0000000..b9b5ad2
--- /dev/null
+++ b/apps/CustomLocale/res/values/colors.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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>
+    <color name="header_background">#888</color>
+</resources>
diff --git a/apps/CustomLocale/res/values/strings.xml b/apps/CustomLocale/res/values/strings.xml
new file mode 100644
index 0000000..313fb87
--- /dev/null
+++ b/apps/CustomLocale/res/values/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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>
+    <string name="app_name">Custom Locale</string>
+    <string name="locale_default">ex_EX</string>
+    <string name="add_new_locale_button">Add New Locale</string>
+    <string name="new_locale_label">New locale code:</string>
+    <string name="add_button">Add</string>
+    <string name="no_data_label">No data</string>
+    <string name="header_current_locale">Current Locale</string>
+    <string name="header_locale_list">Locale List</string>
+    <string name="add_select_button">Add and Select</string>
+</resources>
diff --git a/apps/CustomLocale/res/values/styles.xml b/apps/CustomLocale/res/values/styles.xml
new file mode 100644
index 0000000..8ccc11c
--- /dev/null
+++ b/apps/CustomLocale/res/values/styles.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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>
+    <style name="TextAppearance"
+           parent="android:TextAppearance" />
+
+    <style name="TextAppearance.header">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textStyle">bold</item>
+    </style>
+
+</resources>
diff --git a/apps/CustomLocale/src/com/android/customlocale/CustomLocaleActivity.java b/apps/CustomLocale/src/com/android/customlocale/CustomLocaleActivity.java
new file mode 100644
index 0000000..768f910
--- /dev/null
+++ b/apps/CustomLocale/src/com/android/customlocale/CustomLocaleActivity.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2009 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.customlocale;
+
+
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.ListActivity;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.Button;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.SimpleAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Displays the list of system locales as well as maintain a custom list of user
+ * locales. The user can select a locale and apply it or it can create or remove
+ * a custom locale.
+ */
+public class CustomLocaleActivity extends ListActivity {
+
+    private static final String CUSTOM_LOCALES_SEP = " ";
+    private static final String CUSTOM_LOCALES = "custom_locales";
+    private static final String KEY_CUSTOM = "custom";
+    private static final String KEY_NAME = "name";
+    private static final String KEY_CODE = "code";
+
+    private static final String TAG = "LocaleSetup";
+    private static final boolean DEBUG = true;
+
+    /** Request code returned when the NewLocaleDialog activity finishes. */
+    private static final int UPDATE_LIST = 42;
+    /** Menu item id for applying a locale */
+    private static final int MENU_APPLY = 43;
+    /** Menu item id for removing a custom locale */
+    private static final int MENU_REMOVE = 44;
+
+    /** List view displaying system and custom locales. */
+    private ListView mListView;
+    /** Textview used to display current locale */
+    private TextView mCurrentLocaleTextView;
+    /** Private shared preferences of this activity. */
+    private SharedPreferences mPrefs;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        mPrefs = getPreferences(MODE_PRIVATE);
+
+        Button newLocaleButton = (Button) findViewById(R.id.new_locale);
+
+        newLocaleButton.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                Intent i = new Intent(CustomLocaleActivity.this, NewLocaleDialog.class);
+                startActivityForResult(i, UPDATE_LIST);
+            }
+        });
+
+        mListView = (ListView) findViewById(android.R.id.list);
+        mListView.setFocusable(true);
+        mListView.setFocusableInTouchMode(true);
+        mListView.requestFocus();
+        registerForContextMenu(mListView);
+        setupLocaleList();
+
+        mCurrentLocaleTextView = (TextView) findViewById(R.id.current_locale);
+        displayCurrentLocale();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        if (requestCode == UPDATE_LIST && resultCode == RESULT_OK && data != null) {
+            String locale = data.getExtras().getString(NewLocaleDialog.INTENT_EXTRA_LOCALE);
+            if (locale != null && locale.length() > 0) {
+                // Get current custom locale list
+                String customLocales = mPrefs.getString(CUSTOM_LOCALES, null);
+
+                // Update
+                if (customLocales == null) {
+                    customLocales = locale;
+                } else {
+                    customLocales += CUSTOM_LOCALES_SEP + locale;
+                }
+
+                // Save prefs
+                if (DEBUG) {
+                    Log.d(TAG, "add/customLocales: " + customLocales);
+                }
+                mPrefs.edit().putString(CUSTOM_LOCALES, customLocales).commit();
+
+                Toast.makeText(this, "Added custom locale: " + locale, Toast.LENGTH_SHORT).show();
+
+                // Update list view
+                setupLocaleList();
+
+                // Find the item to select it in the list view
+                ListAdapter a = mListView.getAdapter();
+                for (int i = 0; i < a.getCount(); i++) {
+                    Object o = a.getItem(i);
+                    if (o instanceof Map<?, ?>) {
+                        String code = ((Map<String, String>) o).get(KEY_CODE);
+                        if (code != null && code.equals(locale)) {
+                            mListView.setSelection(i);
+                            break;
+                        }
+                    }
+                }
+
+                if (data.getExtras().getBoolean(NewLocaleDialog.INTENT_EXTRA_SELECT)) {
+                    selectLocale(locale);
+                }
+            }
+        }
+    }
+
+    private void setupLocaleList() {
+        if (DEBUG) {
+            Log.d(TAG, "Update locate list");
+        }
+
+        ArrayList<Map<String, String>> data = new ArrayList<Map<String, String>>();
+
+        // Insert all system locales
+        String[] locales = getAssets().getLocales();
+        for (String locale : locales) {
+            Locale loc = new Locale(locale);
+
+            Map<String, String> map = new HashMap<String, String>(1);
+            map.put(KEY_CODE, locale);
+            map.put(KEY_NAME, loc.getDisplayName());
+            data.add(map);
+        }
+        locales = null;
+
+        // Insert all custom locales
+        String customLocales = mPrefs.getString(CUSTOM_LOCALES, "");
+        if (DEBUG) {
+            Log.d(TAG, "customLocales: " + customLocales);
+        }
+        for (String locale : customLocales.split(CUSTOM_LOCALES_SEP)) {
+            if (locale != null && locale.length() > 0) {
+                Locale loc = new Locale(locale);
+
+                Map<String, String> map = new HashMap<String, String>(1);
+                map.put(KEY_CODE, locale);
+                map.put(KEY_NAME, loc.getDisplayName() + " [Custom]");
+                // the presence of the "custom" key marks it as custom.
+                map.put(KEY_CUSTOM, "");
+                data.add(map);
+            }
+        }
+
+        // Sort all locales by code
+        Collections.sort(data, new Comparator<Map<String, String>>() {
+            public int compare(Map<String, String> lhs, Map<String, String> rhs) {
+                return lhs.get(KEY_CODE).compareTo(rhs.get(KEY_CODE));
+            }
+        });
+
+        // Update the list view adapter
+        mListView.setAdapter(new SimpleAdapter(this, data, R.layout.list_item, new String[] {
+                KEY_CODE, KEY_NAME}, new int[] {R.id.locale_code, R.id.locale_name}));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+        super.onCreateContextMenu(menu, v, menuInfo);
+
+        if (menuInfo instanceof AdapterContextMenuInfo) {
+            int position = ((AdapterContextMenuInfo) menuInfo).position;
+            Object o = mListView.getItemAtPosition(position);
+            if (o instanceof Map<?, ?>) {
+                String locale = ((Map<String, String>) o).get(KEY_CODE);
+                String custom = ((Map<String, String>) o).get(KEY_CUSTOM);
+
+                if (custom == null) {
+                    menu.setHeaderTitle("System Locale");
+                    menu.add(0, MENU_APPLY, 0, "Apply");
+                } else {
+                    menu.setHeaderTitle("Custom Locale");
+                    menu.add(0, MENU_APPLY, 0, "Apply");
+                    menu.add(0, MENU_REMOVE, 0, "Remove");
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public boolean onContextItemSelected(MenuItem item) {
+
+        String pendingLocale = null;
+        boolean is_custom = false;
+
+        ContextMenuInfo menuInfo = item.getMenuInfo();
+        if (menuInfo instanceof AdapterContextMenuInfo) {
+            int position = ((AdapterContextMenuInfo) menuInfo).position;
+            Object o = mListView.getItemAtPosition(position);
+            if (o instanceof Map<?, ?>) {
+                pendingLocale = ((Map<String, String>) o).get(KEY_CODE);
+                is_custom = ((Map<String, String>) o).get(KEY_CUSTOM) != null;
+            }
+        }
+
+        if (pendingLocale == null) {
+            // should never happen
+            return super.onContextItemSelected(item);
+        }
+
+        if (item.getItemId() == MENU_REMOVE) {
+            // Get current custom locale list
+            String customLocales = mPrefs.getString(CUSTOM_LOCALES, "");
+
+            if (DEBUG) {
+                Log.d(TAG, "Remove " + pendingLocale + " from custom locales: " + customLocales);
+            }
+
+            // Update
+            StringBuilder sb = new StringBuilder();
+            for (String locale : customLocales.split(CUSTOM_LOCALES_SEP)) {
+                if (locale != null && locale.length() > 0 && !locale.equals(pendingLocale)) {
+                    if (sb.length() > 0) {
+                        sb.append(CUSTOM_LOCALES_SEP);
+                    }
+                    sb.append(locale);
+                }
+            }
+            String newLocales = sb.toString();
+            if (!newLocales.equals(customLocales)) {
+                // Save prefs
+                mPrefs.edit().putString(CUSTOM_LOCALES, customLocales).commit();
+
+                Toast.makeText(this, "Removed custom locale: " + pendingLocale, Toast.LENGTH_SHORT)
+                        .show();
+            }
+
+        } else if (item.getItemId() == MENU_APPLY) {
+            selectLocale(pendingLocale);
+        }
+
+        return super.onContextItemSelected(item);
+    }
+
+    private void selectLocale(String locale) {
+        if (DEBUG) {
+            Log.d(TAG, "Select locale " + locale);
+        }
+
+        try {
+            IActivityManager am = ActivityManagerNative.getDefault();
+            Configuration config = am.getConfiguration();
+
+            Locale loc = new Locale(locale);
+            config.locale = loc;
+
+            // indicate this isn't some passing default - the user wants this
+            // remembered
+            config.userSetLocale = true;
+
+            am.updateConfiguration(config);
+
+            Toast.makeText(this, "Select locale: " + locale, Toast.LENGTH_SHORT).show();
+        } catch (RemoteException e) {
+            if (DEBUG) {
+                Log.e(TAG, "Select locale failed", e);
+            }
+        }
+    }
+
+    private void displayCurrentLocale() {
+        try {
+            IActivityManager am = ActivityManagerNative.getDefault();
+            Configuration config = am.getConfiguration();
+
+            if (config.locale != null) {
+                String text = String.format("%s - %s",
+                        config.locale.toString(),
+                        config.locale.getDisplayName());
+                mCurrentLocaleTextView.setText(text);
+            }
+        } catch (RemoteException e) {
+            if (DEBUG) {
+                Log.e(TAG, "get current locale failed", e);
+            }
+        }
+    }
+}
diff --git a/apps/CustomLocale/src/com/android/customlocale/NewLocaleDialog.java b/apps/CustomLocale/src/com/android/customlocale/NewLocaleDialog.java
new file mode 100644
index 0000000..3626f73
--- /dev/null
+++ b/apps/CustomLocale/src/com/android/customlocale/NewLocaleDialog.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2009 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.customlocale;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+
+/**
+ * Dialog to ask the user for a new locale. <p/> Returns the locale code (e.g.
+ * "en_US") via an intent with a "locale" extra string and a "select" extra
+ * boolean.
+ */
+public class NewLocaleDialog extends Activity
+    implements View.OnClickListener, View.OnKeyListener {
+
+    public static final String INTENT_EXTRA_LOCALE = "locale";
+    public static final String INTENT_EXTRA_SELECT = "select";
+
+    private static final String TAG = "NewLocale";
+    private static final boolean DEBUG = true;
+    private Button mButtonAdd;
+    private Button mButtonAddSelect;
+    private EditText mEditText;
+    private boolean mWasEmpty;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.new_locale);
+
+        mEditText = (EditText) findViewById(R.id.value);
+        mWasEmpty = true;
+
+        mButtonAdd = (Button) findViewById(R.id.add);
+        mButtonAdd.setOnClickListener(this);
+        mButtonAdd.setEnabled(false);
+
+        mButtonAddSelect = (Button) findViewById(R.id.add_and_select);
+        mButtonAddSelect.setOnClickListener(this);
+        mButtonAddSelect.setEnabled(false);
+        
+        mEditText.setOnKeyListener(this);
+    }
+
+    public void onClick(View v) {
+        String locale = mEditText.getText().toString();
+        boolean select = v == mButtonAddSelect;
+        
+        if (DEBUG) {
+            Log.d(TAG, "New Locale: " + locale + (select ? " + select" : ""));
+        }
+
+        Intent data = new Intent(NewLocaleDialog.this, NewLocaleDialog.class);
+        data.putExtra(INTENT_EXTRA_LOCALE, locale);
+        data.putExtra(INTENT_EXTRA_SELECT, select);
+        setResult(RESULT_OK, data);
+
+        finish();
+    }
+
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        boolean isEmpty = TextUtils.isEmpty(mEditText.getText());
+        if (isEmpty != mWasEmpty) {
+            mWasEmpty = isEmpty;
+            
+            mButtonAdd.setEnabled(!isEmpty);
+            mButtonAddSelect.setEnabled(!isEmpty);
+        }
+        return false;
+    }
+}
diff --git a/apps/SdkSetup/Android.mk b/apps/SdkSetup/Android.mk
index b89baaf..538c2cb 100644
--- a/apps/SdkSetup/Android.mk
+++ b/apps/SdkSetup/Android.mk
@@ -1,7 +1,7 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_MODULE_TAGS := foo
+LOCAL_MODULE_TAGS := user
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
diff --git a/apps/SpareParts/res/values/strings.xml b/apps/SpareParts/res/values/strings.xml
index 42234c1..21c2df9 100644
--- a/apps/SpareParts/res/values/strings.xml
+++ b/apps/SpareParts/res/values/strings.xml
@@ -20,6 +20,17 @@
 <resources>
     <string name="app_label">Spare Parts</string>
 
+    <string name="device_info_title">Device info</string>
+    
+    <string name="title_battery_history">Battery history</string>
+    <string name="summary_battery_history">Summary of how battery has been used</string>
+    
+    <string name="title_battery_information">Battery information</string>
+    <string name="summary_battery_information">Current battery status information</string>
+    
+    <string name="title_usage_statistics">Usage statistics</string>
+    <string name="summary_usage_statistics">Summary of application usage</string>
+    
     <string name="general_title">General</string>
     
     <string name="title_window_animations">Window animations</string>
@@ -48,7 +59,7 @@
     
     <string name="title_accelerometer">Display rotation</string>
     <string name="summary_on_accelerometer">Display rotates from orientation</string>
-    <string name="summary_off_accelerometer">Display rotates when lid is open</string>
+    <string name="summary_off_accelerometer">Display rotates from orientation</string>
     
     <string name="applications_title">Applications</string>
     
diff --git a/apps/SpareParts/res/xml/spare_parts.xml b/apps/SpareParts/res/xml/spare_parts.xml
index 87525c9..5141e22 100644
--- a/apps/SpareParts/res/xml/spare_parts.xml
+++ b/apps/SpareParts/res/xml/spare_parts.xml
@@ -19,6 +19,35 @@
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
 
     <PreferenceCategory
+        android:title="@string/device_info_title">
+
+        <PreferenceScreen android:key="battery_history_settings"
+                android:title="@string/title_battery_history" 
+                android:summary="@string/summary_battery_history">
+            <intent android:action="android.intent.action.MAIN"
+                    android:targetPackage="com.android.settings"
+                    android:targetClass="com.android.settings.battery_history.BatteryHistory" />
+        </PreferenceScreen>
+        
+        <PreferenceScreen android:key="battery_information_settings"
+                android:title="@string/title_battery_information" 
+                android:summary="@string/summary_battery_information">
+            <intent android:action="android.intent.action.MAIN"
+                    android:targetPackage="com.android.settings"
+                    android:targetClass="com.android.settings.BatteryInfo" />
+        </PreferenceScreen>
+        
+        <PreferenceScreen android:key="usage_statistics_settings"
+                android:title="@string/title_usage_statistics" 
+                android:summary="@string/summary_usage_statistics">
+            <intent android:action="android.intent.action.MAIN"
+                    android:targetPackage="com.android.settings"
+                    android:targetClass="com.android.settings.UsageStats" />
+        </PreferenceScreen>
+        
+    </PreferenceCategory>
+            
+    <PreferenceCategory
         android:title="@string/general_title">
         
         <ListPreference
diff --git a/apps/SpareParts/src/com/android/spare_parts/SpareParts.java b/apps/SpareParts/src/com/android/spare_parts/SpareParts.java
index 0f2347e..4852ec2 100644
--- a/apps/SpareParts/src/com/android/spare_parts/SpareParts.java
+++ b/apps/SpareParts/src/com/android/spare_parts/SpareParts.java
@@ -19,7 +19,11 @@
 
 import android.app.ActivityManagerNative;
 import android.content.Context;
+import android.content.Intent;
 import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.os.RemoteException;
@@ -28,6 +32,7 @@
 import android.preference.ListPreference;
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
+import android.preference.PreferenceGroup;
 import android.preference.PreferenceScreen;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
@@ -35,11 +40,17 @@
 import android.util.Log;
 import android.view.IWindowManager;
 
+import java.util.List;
+
 public class SpareParts extends PreferenceActivity
         implements Preference.OnPreferenceChangeListener,
         SharedPreferences.OnSharedPreferenceChangeListener {
     private static final String TAG = "SpareParts";
 
+    private static final String BATTERY_HISTORY_PREF = "battery_history_settings";
+    private static final String BATTERY_INFORMATION_PREF = "battery_information_settings";
+    private static final String USAGE_STATISTICS_PREF = "usage_statistics_settings";
+    
     private static final String WINDOW_ANIMATIONS_PREF = "window_animations";
     private static final String TRANSITION_ANIMATIONS_PREF = "transition_animations";
     private static final String FANCY_IME_ANIMATIONS_PREF = "fancy_ime_animations";
@@ -62,6 +73,41 @@
     
     private IWindowManager mWindowManager;
 
+    public static boolean updatePreferenceToSpecificActivityOrRemove(Context context,
+            PreferenceGroup parentPreferenceGroup, String preferenceKey, int flags) {
+        
+        Preference preference = parentPreferenceGroup.findPreference(preferenceKey);
+        if (preference == null) {
+            return false;
+        }
+        
+        Intent intent = preference.getIntent();
+        if (intent != null) {
+            // Find the activity that is in the system image
+            PackageManager pm = context.getPackageManager();
+            List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
+            int listSize = list.size();
+            for (int i = 0; i < listSize; i++) {
+                ResolveInfo resolveInfo = list.get(i);
+                if ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
+                        != 0) {
+                    
+                    // Replace the intent with this specific activity
+                    preference.setIntent(new Intent().setClassName(
+                            resolveInfo.activityInfo.packageName,
+                            resolveInfo.activityInfo.name));
+                    
+                    return true;
+                }
+            }
+        }
+
+        // Did not find a matching activity, so remove the preference
+        parentPreferenceGroup.removePreference(preference);
+        
+        return true;
+    }
+    
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
@@ -84,7 +130,15 @@
         
         mWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
         
-        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
+        final PreferenceGroup parentPreference = getPreferenceScreen();
+        updatePreferenceToSpecificActivityOrRemove(this, parentPreference,
+                BATTERY_HISTORY_PREF, 0);
+        updatePreferenceToSpecificActivityOrRemove(this, parentPreference,
+                BATTERY_INFORMATION_PREF, 0);
+        updatePreferenceToSpecificActivityOrRemove(this, parentPreference,
+                USAGE_STATISTICS_PREF, 0);
+        
+        parentPreference.getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
     }
 
     private void updateToggles() {
diff --git a/build/sdk.atree b/build/sdk.atree
index 4fb768b..0f62ea0 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -66,6 +66,7 @@
 development/samples/ApiDemos platforms/${PLATFORM_NAME}/samples/ApiDemos
 development/samples/SkeletonApp platforms/${PLATFORM_NAME}/samples/SkeletonApp
 development/samples/Snake platforms/${PLATFORM_NAME}/samples/Snake
+development/samples/SoftKeyboard platforms/${PLATFORM_NAME}/samples/SoftKeyboard
 
 # dx
 bin/dx platforms/${PLATFORM_NAME}/tools/dx
diff --git a/build/tools/make_windows_sdk.sh b/build/tools/make_windows_sdk.sh
index a32546c..dedf3a7 100755
--- a/build/tools/make_windows_sdk.sh
+++ b/build/tools/make_windows_sdk.sh
@@ -146,6 +146,11 @@
     echo "Done"
     echo
     echo "Resulting SDK is in $DIST_DIR/$DEST_NAME_ZIP"
+
+    # We want fastboot and adb next to the new SDK
+    for i in fastboot.exe adb.exe AdbWinApi.dll; do
+        mv -vf out/host/windows-x86/bin/$i "$DIST_DIR"/$i
+    done
 }
 
 check
diff --git a/pdk/docs/templates/footer.cs b/pdk/docs/templates/footer.cs
new file mode 100755
index 0000000..bb82c8d
--- /dev/null
+++ b/pdk/docs/templates/footer.cs
@@ -0,0 +1,19 @@
+<div id="footer">
+
+<?cs if:reference||guide ?>
+  <div id="copyright">
+    <?cs call:custom_copyright() ?>
+  </div>
+  <div id="build_info">
+    <?cs call:custom_buildinfo() ?>
+  </div>
+<?cs elif:!hide_license_footer ?>
+  <div id="copyright">
+    <?cs call:custom_cc_copyright() ?>
+  </div>
+<?cs /if ?>
+  <div id="footerlinks">
+    <?cs call:custom_footerlinks() ?>
+  </div>
+
+</div> <!-- end footer -->
diff --git a/pdk/docs/templates/head_tag.cs b/pdk/docs/templates/head_tag.cs
new file mode 100755
index 0000000..55d0225
--- /dev/null
+++ b/pdk/docs/templates/head_tag.cs
@@ -0,0 +1,37 @@
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<link rel="shortcut icon" type="image/x-icon" href="<?cs var:toroot ?>favicon.ico" />
+<title><?cs 
+  if:page.title ?><?cs 
+    var:page.title ?><?cs
+    if:sdk.version ?> (<?cs
+      var:sdk.version ?>)<?cs
+    /if ?> | <?cs
+  /if ?>Android Developers</title><?cs 
+if:guide||sdk ?>
+<link href="<?cs var:toroot ?>assets/android-developer-docs-devguide.css" rel="stylesheet" type="text/css" /><?cs 
+else ?>
+<link href="<?cs var:toroot ?>assets/android-developer-docs.css" rel="stylesheet" type="text/css" /><?cs 
+/if ?>
+<script src="<?cs var:toroot ?>assets/search_autocomplete.js" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>reference/lists.js" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>assets/jquery-resizable.min.js" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>assets/android-developer-docs.js" type="text/javascript"></script>
+<script type="text/javascript">
+  setToRoot("<?cs var:toroot ?>");
+</script><?cs 
+if:reference ?>
+<script src="<?cs var:toroot ?>navtree_data.js" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>assets/navtree.js" type="text/javascript"></script><?cs 
+/if ?>
+<noscript>
+  <style type="text/css">
+    body{overflow:auto;}
+    #body-content{position:relative; top:0;}
+    #doc-content{overflow:visible;border-left:3px solid #666;}
+    #side-nav{padding:0;}
+    #side-nav .toggle-list ul {display:block;}
+    #resize-packages-nav{border-bottom:3px solid #666;}
+  </style>
+</noscript>
+</head>
diff --git a/pdk/docs/templates/header.cs b/pdk/docs/templates/header.cs
new file mode 100755
index 0000000..e8301be
--- /dev/null
+++ b/pdk/docs/templates/header.cs
@@ -0,0 +1,3 @@
+<?cs call:custom_masthead() ?>
+<?cs call:custom_left_nav() ?>
+
diff --git a/pdk/docs/templates/index.cs b/pdk/docs/templates/index.cs
new file mode 100755
index 0000000..15a6a59
--- /dev/null
+++ b/pdk/docs/templates/index.cs
@@ -0,0 +1,8 @@
+<html>
+<head>
+<meta http-equiv="refresh" content="0;url=packages.html">
+</head>
+<body>
+<?cs include:"analytics.cs" ?>
+</body>
+</html>
\ No newline at end of file
diff --git a/pdk/docs/templates/trailer.cs b/pdk/docs/templates/trailer.cs
new file mode 100755
index 0000000..155ba58
--- /dev/null
+++ b/pdk/docs/templates/trailer.cs
@@ -0,0 +1,11 @@
+</div> <!-- end body-content --> <?cs # normally opened by header.cs ?>
+
+<script type="text/javascript">
+init(); /* initialize android-developer-docs.js */
+var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
+document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
+</script>
+<script type="text/javascript">
+var pageTracker = _gat._getTracker("UA-5831155-1");
+pageTracker._trackPageview();
+</script>
\ No newline at end of file
diff --git a/samples/Home/src/com/example/android/home/ApplicationsStackLayout.java b/samples/Home/src/com/example/android/home/ApplicationsStackLayout.java
index a2073fa..ccc1f43 100644
--- a/samples/Home/src/com/example/android/home/ApplicationsStackLayout.java
+++ b/samples/Home/src/com/example/android/home/ApplicationsStackLayout.java
@@ -105,7 +105,7 @@
 
         a.recycle();
 
-        mIconSize = (int) getResources().getDimension(android.R.dimen.app_icon_size);
+        mIconSize = 42; //(int) getResources().getDimension(android.R.dimen.app_icon_size);
 
         initLayout();
     }
diff --git a/samples/Home/src/com/example/android/home/Home.java b/samples/Home/src/com/example/android/home/Home.java
index de67ef4..7e94cc3 100644
--- a/samples/Home/src/com/example/android/home/Home.java
+++ b/samples/Home/src/com/example/android/home/Home.java
@@ -21,28 +21,26 @@
 import android.app.SearchManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.PaintFlagsDrawFilter;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.graphics.ColorFilter;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.PaintDrawable;
 import android.os.Bundle;
-import android.os.Handler;
+import android.os.Environment;
 import android.util.Log;
+import android.util.Xml;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -57,14 +55,19 @@
 import android.widget.CheckBox;
 import android.widget.GridView;
 import android.widget.TextView;
-import android.net.Uri;
 
 import java.io.IOException;
+import java.io.FileReader;
+import java.io.File;
+import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 public class Home extends Activity {
     /**
      * Tag used for logging errors.
@@ -76,6 +79,13 @@
      */
     private static final String KEY_SAVE_GRID_OPENED = "grid.opened";
 
+    private static final String DEFAULT_FAVORITES_PATH = "etc/favorites.xml";
+
+    private static final String TAG_FAVORITES = "favorites";
+    private static final String TAG_FAVORITE = "favorite";
+    private static final String TAG_PACKAGE = "package";
+    private static final String TAG_CLASS = "class";    
+
     // Identifiers for option menu items
     private static final int MENU_WALLPAPER_SETTINGS = Menu.FIRST + 1;
     private static final int MENU_SEARCH = MENU_WALLPAPER_SETTINGS + 1;
@@ -90,11 +100,8 @@
     private static ArrayList<ApplicationInfo> mApplications;
     private static LinkedList<ApplicationInfo> mFavorites;
 
-    private Handler mHandler = new Handler();
-
     private final BroadcastReceiver mWallpaperReceiver = new WallpaperIntentReceiver();
     private final BroadcastReceiver mApplicationsReceiver = new ApplicationsIntentReceiver();
-    private final ContentObserver mObserver = new FavoritesChangeObserver();
 
     private GridView mGrid;
 
@@ -120,7 +127,6 @@
         setContentView(R.layout.home);
 
         registerIntentReceivers();
-        registerContentObservers();
 
         setDefaultWallpaper();
 
@@ -156,7 +162,6 @@
             mApplications.get(i).icon.setCallback(null);
         }
 
-        getContentResolver().unregisterContentObserver(mObserver);
         unregisterReceiver(mWallpaperReceiver);
         unregisterReceiver(mApplicationsReceiver);
     }
@@ -199,16 +204,6 @@
     }
 
     /**
-     * Registers various content observers. The current implementation registers
-     * only a favorites observer to keep track of the favorites applications.
-     */
-    private void registerContentObservers() {
-        ContentResolver resolver = getContentResolver();
-        resolver.registerContentObserver(Uri.parse("content://" +
-                android.provider.Settings.AUTHORITY + "/favorites?notify=true"), true, mObserver);
-    }
-
-    /**
      * Creates a new appplications adapter for the grid view and registers it.
      */
     private void bindApplications() {
@@ -247,7 +242,7 @@
                     Log.e(LOG_TAG, "Failed to clear wallpaper " + e);
                 }
             } else {
-                getWindow().setBackgroundDrawable(wallpaper);
+                getWindow().setBackgroundDrawable(new ClippedDrawable(wallpaper));
             }
             mWallpaperChecked = true;
         }
@@ -259,18 +254,17 @@
      */
     private void bindFavorites(boolean isLaunching) {
         if (!isLaunching || mFavorites == null) {
-            final Cursor c = getContentResolver().query(Uri.parse("content://" +
-                android.provider.Settings.AUTHORITY + "/favorites?notify=true"),
-                    null, null, null, "cellX");
 
-            final int intentIndex = c.getColumnIndexOrThrow("intent");
-            final int titleIndex = c.getColumnIndexOrThrow("title");
-            final int typeIndex = c.getColumnIndexOrThrow("itemType");
+            FileReader favReader;
 
-            final PackageManager manager = getPackageManager();
-
-            ApplicationInfo info;
-            String intentDescription;
+            // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
+            final File favFile = new File(Environment.getRootDirectory(), DEFAULT_FAVORITES_PATH);
+            try {
+                favReader = new FileReader(favFile);
+            } catch (FileNotFoundException e) {
+                Log.e(LOG_TAG, "Couldn't find or open favorites file " + favFile);
+                return;
+            }
 
             if (mFavorites == null) {
                 mFavorites = new LinkedList<ApplicationInfo>();
@@ -278,38 +272,77 @@
                 mFavorites.clear();
             }
 
-            while (c.moveToNext()) {
-                final int itemType = c.getInt(typeIndex);
+            final Intent intent = new Intent(Intent.ACTION_MAIN, null);
+            intent.addCategory(Intent.CATEGORY_LAUNCHER);
 
-                if (itemType == 0 || // 0 == application
-                    itemType == 1) { // 1 == shortcut
+            final PackageManager packageManager = getPackageManager();
 
-                    intentDescription = c.getString(intentIndex);
-                    if (intentDescription == null) {
-                        continue;
+            try {
+                final XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(favReader);
+
+                beginDocument(parser, TAG_FAVORITES);
+
+                ApplicationInfo info;
+
+                while (true) {
+                    nextElement(parser);
+
+                    String name = parser.getName();
+                    if (!TAG_FAVORITE.equals(name)) {
+                        break;
                     }
 
-                    Intent intent;
-                    try {
-                        intent = Intent.getIntent(intentDescription);
-                    } catch (java.net.URISyntaxException e) {
-                        continue;
-                    }
-                    info = getApplicationInfo(manager, intent);
+                    final String favoritePackage = parser.getAttributeValue(null, TAG_PACKAGE);
+                    final String favoriteClass = parser.getAttributeValue(null, TAG_CLASS);
+
+                    final ComponentName cn = new ComponentName(favoritePackage, favoriteClass);
+                    intent.setComponent(cn);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+                    info = getApplicationInfo(packageManager, intent);
                     if (info != null) {
-                        info.title = c.getString(titleIndex);
                         info.intent = intent;
                         mFavorites.addFirst(info);
                     }
                 }
+            } catch (XmlPullParserException e) {
+                Log.w(LOG_TAG, "Got exception parsing favorites.", e);
+            } catch (IOException e) {
+                Log.w(LOG_TAG, "Got exception parsing favorites.", e);
             }
-
-            c.close();
         }
 
         mApplicationsStack.setFavorites(mFavorites);
     }
 
+    private static void beginDocument(XmlPullParser parser, String firstElementName)
+            throws XmlPullParserException, IOException {
+
+        int type;
+        while ((type = parser.next()) != XmlPullParser.START_TAG &&
+                type != XmlPullParser.END_DOCUMENT) {
+            // Empty
+        }
+
+        if (type != XmlPullParser.START_TAG) {
+            throw new XmlPullParserException("No start tag found");
+        }
+
+        if (!parser.getName().equals(firstElementName)) {
+            throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
+                    ", expected " + firstElementName);
+        }
+    }
+
+    private static void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException {
+        int type;
+        while ((type = parser.next()) != XmlPullParser.START_TAG &&
+                type != XmlPullParser.END_DOCUMENT) {
+            // Empty
+        }
+    }
+
     /**
      * Refreshes the recently launched applications stacked over the favorites. The number
      * of recents depends on how many favorites are present.
@@ -361,14 +394,6 @@
         return info;
     }
 
-    /**
-     * When the notification that favorites have changed is received, requests
-     * a favorites list refresh.
-     */
-    private void onFavoritesChanged() {
-        bindFavorites(false);
-    }
-
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
         if (event.getAction() == KeyEvent.ACTION_DOWN) {
@@ -530,7 +555,7 @@
     private class WallpaperIntentReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            getWindow().setBackgroundDrawable(getWallpaper());
+            getWindow().setBackgroundDrawable(new ClippedDrawable(getWallpaper()));
         }
     }
 
@@ -548,20 +573,6 @@
     }
 
     /**
-     * Receives notifications whenever the user favorites have changed.
-     */
-    private class FavoritesChangeObserver extends ContentObserver {
-        public FavoritesChangeObserver() {
-            super(mHandler);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            onFavoritesChanged();
-        }
-    }
-
-    /**
      * GridView adapter to show the list of all installed applications.
      */
     private class ApplicationsAdapter extends ArrayAdapter<ApplicationInfo> {
@@ -580,13 +591,12 @@
                 convertView = inflater.inflate(R.layout.application, parent, false);
             }
 
-            //final ImageView imageView = (ImageView) convertView.findViewById(R.id.icon);
             Drawable icon = info.icon;
 
             if (!info.filtered) {
-                final Resources resources = getContext().getResources();
-                int width = (int) resources.getDimension(android.R.dimen.app_icon_size);
-                int height = (int) resources.getDimension(android.R.dimen.app_icon_size);
+                //final Resources resources = getContext().getResources();
+                int width = 42;//(int) resources.getDimension(android.R.dimen.app_icon_size);
+                int height = 42;//(int) resources.getDimension(android.R.dimen.app_icon_size);
 
                 final int iconWidth = icon.getIntrinsicWidth();
                 final int iconHeight = icon.getIntrinsicHeight();
@@ -687,4 +697,47 @@
             startActivity(app.intent);
         }
     }
+
+    /**
+     * When a drawable is attached to a View, the View gives the Drawable its dimensions
+     * by calling Drawable.setBounds(). In this application, the View that draws the
+     * wallpaper has the same size as the screen. However, the wallpaper might be larger
+     * that the screen which means it will be automatically stretched. Because stretching
+     * a bitmap while drawing it is very expensive, we use a ClippedDrawable instead.
+     * This drawable simply draws another wallpaper but makes sure it is not stretched
+     * by always giving it its intrinsic dimensions. If the wallpaper is larger than the
+     * screen, it will simply get clipped but it won't impact performance.
+     */
+    private class ClippedDrawable extends Drawable {
+        private final Drawable mWallpaper;
+
+        public ClippedDrawable(Drawable wallpaper) {
+            mWallpaper = wallpaper;
+        }
+
+        @Override
+        public void setBounds(int left, int top, int right, int bottom) {
+            super.setBounds(left, top, right, bottom);
+            // Ensure the wallpaper is as large as it really is, to avoid stretching it
+            // at drawing time
+            mWallpaper.setBounds(left, top, left + mWallpaper.getIntrinsicWidth(),
+                    top + mWallpaper.getIntrinsicHeight());
+        }
+
+        public void draw(Canvas canvas) {
+            mWallpaper.draw(canvas);
+        }
+
+        public void setAlpha(int alpha) {
+            mWallpaper.setAlpha(alpha);
+        }
+
+        public void setColorFilter(ColorFilter cf) {
+            mWallpaper.setColorFilter(cf);
+        }
+
+        public int getOpacity() {
+            return mWallpaper.getOpacity();
+        }
+    }
 }
diff --git a/samples/SoftKeyboard/res/drawable/sym_keyboard_search.png b/samples/SoftKeyboard/res/drawable/sym_keyboard_search.png
new file mode 100755
index 0000000..127755d
--- /dev/null
+++ b/samples/SoftKeyboard/res/drawable/sym_keyboard_search.png
Binary files differ
diff --git a/samples/SoftKeyboard/res/values/strings.xml b/samples/SoftKeyboard/res/values/strings.xml
index c1e306d..bc645b2 100644
--- a/samples/SoftKeyboard/res/values/strings.xml
+++ b/samples/SoftKeyboard/res/values/strings.xml
@@ -25,9 +25,7 @@
     <string name="word_separators">\u0020.,;:!?\n()[]*&amp;@{}/&lt;&gt;_+=|&quot;</string>
     
     <!-- Labels on soft keys -->
-    <string name="label_done">Done</string>
-    <string name="label_search">Search</string>
-    <string name="label_enter">Enter</string>
-    <string name="label_next">Next</string>
-    <string name="label_previous">Previous</string>
+    <string name="label_go_key">Go</string>
+    <string name="label_next_key">Next</string>
+    <string name="label_send_key">Send</string>
 </resources>
diff --git a/samples/SoftKeyboard/src/com/example/android/softkeyboard/LatinKeyboard.java b/samples/SoftKeyboard/src/com/example/android/softkeyboard/LatinKeyboard.java
index 944cefb..1798442 100644
--- a/samples/SoftKeyboard/src/com/example/android/softkeyboard/LatinKeyboard.java
+++ b/samples/SoftKeyboard/src/com/example/android/softkeyboard/LatinKeyboard.java
@@ -20,9 +20,14 @@
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.Keyboard.Key;
+import android.inputmethodservice.Keyboard.Row;
+import android.view.inputmethod.EditorInfo;
 
 public class LatinKeyboard extends Keyboard {
 
+    private Key mEnterKey;
+    
     public LatinKeyboard(Context context, int xmlLayoutResId) {
         super(context, xmlLayoutResId);
     }
@@ -35,7 +40,49 @@
     @Override
     protected Key createKeyFromXml(Resources res, Row parent, int x, int y, 
             XmlResourceParser parser) {
-        return new LatinKey(res, parent, x, y, parser);
+        Key key = new LatinKey(res, parent, x, y, parser);
+        if (key.codes[0] == 10) {
+            mEnterKey = key;
+        }
+        return key;
+    }
+    
+    /**
+     * This looks at the ime options given by the current editor, to set the
+     * appropriate label on the keyboard's enter key (if it has one).
+     */
+    void setImeOptions(Resources res, int options) {
+        if (mEnterKey == null) {
+            return;
+        }
+        
+        switch (options&(EditorInfo.IME_MASK_ACTION|EditorInfo.IME_FLAG_NO_ENTER_ACTION)) {
+            case EditorInfo.IME_ACTION_GO:
+                mEnterKey.iconPreview = null;
+                mEnterKey.icon = null;
+                mEnterKey.label = res.getText(R.string.label_go_key);
+                break;
+            case EditorInfo.IME_ACTION_NEXT:
+                mEnterKey.iconPreview = null;
+                mEnterKey.icon = null;
+                mEnterKey.label = res.getText(R.string.label_next_key);
+                break;
+            case EditorInfo.IME_ACTION_SEARCH:
+                mEnterKey.icon = res.getDrawable(
+                        R.drawable.sym_keyboard_search);
+                mEnterKey.label = null;
+                break;
+            case EditorInfo.IME_ACTION_SEND:
+                mEnterKey.iconPreview = null;
+                mEnterKey.icon = null;
+                mEnterKey.label = res.getText(R.string.label_send_key);
+                break;
+            default:
+                mEnterKey.icon = res.getDrawable(
+                        R.drawable.sym_keyboard_return);
+                mEnterKey.label = null;
+                break;
+        }
     }
     
     static class LatinKey extends Keyboard.Key {
diff --git a/samples/SoftKeyboard/src/com/example/android/softkeyboard/SoftKeyboard.java b/samples/SoftKeyboard/src/com/example/android/softkeyboard/SoftKeyboard.java
index 4df4853..656efdf 100644
--- a/samples/SoftKeyboard/src/com/example/android/softkeyboard/SoftKeyboard.java
+++ b/samples/SoftKeyboard/src/com/example/android/softkeyboard/SoftKeyboard.java
@@ -65,11 +65,11 @@
     private long mLastShiftTime;
     private long mMetaState;
     
-    private Keyboard mSymbolsKeyboard;
-    private Keyboard mSymbolsShiftedKeyboard;
-    private Keyboard mQwertyKeyboard;
+    private LatinKeyboard mSymbolsKeyboard;
+    private LatinKeyboard mSymbolsShiftedKeyboard;
+    private LatinKeyboard mQwertyKeyboard;
     
-    private Keyboard mCurKeyboard;
+    private LatinKeyboard mCurKeyboard;
     
     private String mWordSeparators;
     
@@ -208,6 +208,10 @@
                 // keyboard with no special features.
                 mCurKeyboard = mQwertyKeyboard;
         }
+        
+        // Update the label on the enter key, depending on what the application
+        // says it will do.
+        mCurKeyboard.setImeOptions(getResources(), attribute.imeOptions);
     }
 
     /**
@@ -504,6 +508,18 @@
         }
     }
 
+    public void onText(CharSequence text) {
+        InputConnection ic = getCurrentInputConnection();
+        if (ic == null) return;
+        ic.beginBatchEdit();
+        if (mComposing.length() > 0) {
+            commitTyped(ic);
+        }
+        ic.commitText(text, 0);
+        ic.endBatchEdit();
+        updateShiftKeyState(getCurrentInputEditorInfo());
+    }
+
     /**
      * Update the list of available candidates from the current composing
      * text.  This will need to be filled in by however you are determining
diff --git a/testrunner/tests.xml b/testrunner/tests.xml
new file mode 100644
index 0000000..8d9c0ab
--- /dev/null
+++ b/testrunner/tests.xml
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<!-- 
+This file contains standard test definitions for the Android platform
+          
+Tests are defined by <test> tags with the following attributes
+
+name package [class runner build_path coverage_target continuous]
+
+Where:
+name: Self-descriptive name used to uniquely identify the test
+build_path: File system path, relative to Android build root, to this package's
+   Android.mk file. If omitted, build/sync step for this test will be skipped
+package: Android application package that contains the tests
+class: Optional. Fully qualified Java test class to run. 
+runner: Fully qualified InstrumentationTestRunner to execute. If omitted, 
+   will default to android.test.InstrumentationTestRunner
+coverage_target: Build name of Android package this test targets - these targets
+   are defined in the coverage_targets.xml file.  Used as basis for code
+   coverage metrics. If omitted, code coverage will not be supported for this
+   test
+continuous: Optional boolean. Default is false. Set to true if tests are known
+   to be reliable, and should be included in a continuous test system. false if
+   they are under development.
+
+These attributes map to the following commands:  
+(if class is defined)
+    adb shell am instrument -w <package>/<runner>
+(else)
+    adb shell am instrument -w -e class <class> <package>/<runner>
+
+-->
+
+<test-definitions version="1">
+
+<!-- system-wide tests -->
+<test name="framework"
+    build_path="frameworks/base/tests/FrameworkTest"
+    package="com.android.frameworktest.tests"
+    class="com.android.frameworktest.AllTests"
+    coverage_target="framework"
+    continuous="true" />
+
+<test name="android"
+    build_path="frameworks/base/tests/AndroidTests"
+    package="com.android.unit_tests"
+    class="com.android.unit_tests.AndroidTests"
+    coverage_target="framework"
+    continuous="true" />
+
+<test name="smoke"
+    build_path="frameworks/base/tests/SmokeTest"
+    package="com.android.smoketest.tests"
+    coverage_target="framework"
+    continuous="true" />
+
+<test name="core"
+    build_path="frameworks/base/tests/CoreTests"
+    package="android.core"
+    class="android.core.CoreTests"
+    coverage_target="framework"
+    continuous="true" />
+
+<test name="libcore"
+    build_path="frameworks/base/tests/CoreTests"
+    package="android.core"
+    class="android.core.JavaTests"
+    coverage_target="framework" />
+    
+<test name="apidemos"
+    build_path="development/samples/ApiDemos"
+    package="com.example.android.apis.tests"
+    coverage_target="ApiDemos"
+    continuous="true" />
+
+<!--  targeted framework tests -->
+<test name="heap"
+    build_path="frameworks/base/tests/AndroidTests"
+    package="com.android.unit_tests"
+    class="com.android.unit_tests.HeapTest"
+    coverage_target="framework" />
+
+<test name="activity"
+    build_path="frameworks/base/tests/AndroidTests"
+    package="com.android.unit_tests"
+    class="com.android.unit_tests.activity.ActivityTests"
+    coverage_target="framework" />
+
+<!--  obsolete?
+<test name="deadlock"
+    build_path="frameworks/base/tests/Deadlock"
+    package="com.android.deadlock.tests"
+    coverage_target="framework" />
+ -->
+
+
+<test name="tablemerger"
+    build_path="frameworks/base/tests/FrameworkTest"
+    package="com.android.frameworktest.tests"
+    class="android.content.AbstractTableMergerTest"
+    coverage_target="framework" />
+
+
+<!--  selected app tests -->
+<test name="browser"
+    build_path="packages/apps/Browser"
+    package="com.android.browser"
+    runner=".BrowserTestRunner"
+    coverage_target="Browser" />
+
+<test name="browserfunc"
+    build_path="packages/apps/Browser"
+    package="com.android.browser"
+    runner=".BrowserFunctionalTestRunner"
+    coverage_target="Browser" />
+
+<test name="calendar"
+    build_path="packages/apps/Calendar/tests"
+    package="com.android.calendar.tests"
+    coverage_target="Calendar"
+    continuous="true" />
+
+<test name="calprov"
+    build_path="packages/providers/CalendarProvider/tests"
+    package="com.android.providers.calendar.tests"
+    coverage_target="CalendarProvider"
+    continuous="true" />
+
+<test name="camera"
+    build_path="packages/apps/Camera/tests"
+    package="com.android.cameratests"
+    runner="CameraInstrumentationTestRunner"
+    coverage_target="Camera" />
+
+<test name="contactsprov"
+    build_path="packages/providers/GoogleContactsProvider/tests"
+    package="com.android.providers.contactstests"
+    coverage_target="ContactsProvider" />
+
+<test name="email"
+    build_path="packages/apps/Email"
+    package="com.android.email.tests"
+    coverage_target="Email"
+    continuous="true" />
+
+<test name="emailsmall"
+    build_path="packages/apps/Email"
+    package="com.android.email.tests"
+    class="com.android.email.SmallTests"
+    coverage_target="Email" />
+
+<test name="media"
+    build_path="frameworks/base/media/tests/MediaFrameworkTest"
+    package="com.android.mediaframeworktest"
+    runner=".MediaFrameworkTestRunner"
+    coverage_target="framework"
+    continuous="true" />
+
+<test name="mediaunit"
+    build_path="frameworks/base/media/tests/MediaFrameworkTest"
+    package="com.android.mediaframeworktest"
+    runner=".MediaFrameworkUnitTestRunner"
+    coverage_target="framework" />
+
+<!-- obsolete?
+<test name="mediaprov"
+    build_path="tests/MediaProvider"
+    package="com.android.mediaprovidertests"
+    runner=".MediaProviderTestsInstrumentation"
+    coverage_target="MediaProvider" />
+ -->
+
+<test name="mms"
+    build_path="packages/apps/Mms"
+    package="com.android.mms.tests"
+    runner="com.android.mms.ui.MMSInstrumentationTestRunner"
+    coverage_target="Mms" />
+
+<test name="mmslaunch"
+    build_path="packages/apps/Mms"
+    package="com.android.mms.tests"
+    runner="com.android.mms.SmsLaunchPerformance"
+    coverage_target="Mms" />
+
+
+<!-- obsolete?
+<test name="ringtone"
+    build_path="tests/RingtoneSettings"
+    package="com.android.ringtonesettingstests"
+    runner=".RingtoneSettingsInstrumentationTestRunner"
+    coverage_target="Settings" />
+-->
+
+</test-definitions>
diff --git a/tools/apkbuilder/etc/apkbuilder.bat b/tools/apkbuilder/etc/apkbuilder.bat
index 7ab3e6c..c4689c6 100755
--- a/tools/apkbuilder/etc/apkbuilder.bat
+++ b/tools/apkbuilder/etc/apkbuilder.bat
@@ -20,9 +20,9 @@
 rem and set up progdir to be the fully-qualified pathname of its directory.
 set prog=%~f0
 
-rem Change current directory to where ddms is, to avoid issues with directories
-rem containing whitespaces.
-cd %~dp0
+rem Change current directory and drive to where the script is, to avoid
+rem issues with directories containing whitespaces.
+cd /d %~dp0
 
 set jarfile=apkbuilder.jar
 set frameworkdir=
diff --git a/tools/ddms/app/etc/ddms.bat b/tools/ddms/app/etc/ddms.bat
index 8d941b9..5da9fb5 100755
--- a/tools/ddms/app/etc/ddms.bat
+++ b/tools/ddms/app/etc/ddms.bat
@@ -20,9 +20,9 @@
 rem and set up progdir to be the fully-qualified pathname of its directory.
 set prog=%~f0
 
-rem Change current directory to where ddms is, to avoid issues with directories
-rem containing whitespaces.
-cd %~dp0
+rem Change current directory and drive to where the script is, to avoid
+rem issues with directories containing whitespaces.
+cd /d %~dp0
 
 set jarfile=ddms.jar
 set frameworkdir=
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java
index a4576aa..866d578 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java
@@ -94,7 +94,7 @@
      * is only used for data generated within Client.
      */
     private static final int INITIAL_BUF_SIZE = 2*1024;
-    private static final int MAX_BUF_SIZE = 2*1024*1024;
+    private static final int MAX_BUF_SIZE = 200*1024*1024;
     private ByteBuffer mReadBuffer;
 
     private static final int WRITE_BUF_SIZE = 256;
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java
index 34ef432..0e7f0bb 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Device.java
@@ -30,7 +30,7 @@
 
 /**
  * A Device. It can be a physical device or an emulator.
- * 
+ *
  * TODO: make this class package-protected, and shift all callers to use IDevice
  */
 public final class Device implements IDevice {
@@ -62,10 +62,10 @@
             return null;
         }
     }
-    
+
     /** Emulator Serial Number regexp. */
     final static String RE_EMULATOR_SN = "emulator-(\\d+)"; //$NON-NLS-1$
-    
+
     /** Serial number of the device */
     String serialNumber = null;
 
@@ -74,7 +74,7 @@
 
     /** State of the device. */
     DeviceState state = null;
-    
+
     /** Device properties. */
     private final Map<String, String> mProperties = new HashMap<String, String>();
 
@@ -85,29 +85,29 @@
      * Socket for the connection monitoring client connection/disconnection.
      */
     private SocketChannel mSocketChannel;
-    
-    /* 
+
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#getSerialNumber()
      */
     public String getSerialNumber() {
         return serialNumber;
     }
-    
+
     public String getAvdName() {
         return mAvdName;
     }
 
-    
-    /* 
+
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#getState()
      */
     public DeviceState getState() {
         return state;
     }
-    
-    /* 
+
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#getProperties()
      */
@@ -115,7 +115,7 @@
         return Collections.unmodifiableMap(mProperties);
     }
 
-    /* 
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#getPropertyCount()
      */
@@ -123,21 +123,21 @@
         return mProperties.size();
     }
 
-    /* 
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#getProperty(java.lang.String)
      */
     public String getProperty(String name) {
         return mProperties.get(name);
     }
-    
+
 
     @Override
     public String toString() {
         return serialNumber;
     }
 
-    /* 
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#isOnline()
      */
@@ -145,7 +145,7 @@
         return state == DeviceState.ONLINE;
     }
 
-    /* 
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#isEmulator()
      */
@@ -153,7 +153,7 @@
         return serialNumber.matches(RE_EMULATOR_SN);
     }
 
-    /* 
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#isOffline()
      */
@@ -161,7 +161,7 @@
         return state == DeviceState.OFFLINE;
     }
 
-    /* 
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#isBootLoader()
      */
@@ -169,7 +169,7 @@
         return state == DeviceState.BOOTLOADER;
     }
 
-    /* 
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#hasClients()
      */
@@ -177,7 +177,7 @@
         return mClients.size() > 0;
     }
 
-    /* 
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#getClients()
      */
@@ -186,8 +186,8 @@
             return mClients.toArray(new Client[mClients.size()]);
         }
     }
-    
-    /* 
+
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#getClient(java.lang.String)
      */
@@ -204,7 +204,7 @@
         return null;
     }
 
-    /* 
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#getSyncService()
      */
@@ -217,7 +217,7 @@
         return null;
     }
 
-    /* 
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#getFileListingService()
      */
@@ -225,7 +225,7 @@
         return new FileListingService(this);
     }
 
-    /* 
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#getScreenshot()
      */
@@ -233,7 +233,7 @@
         return AdbHelper.getFrameBuffer(AndroidDebugBridge.sSocketAddr, this);
     }
 
-    /* 
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#executeShellCommand(java.lang.String, com.android.ddmlib.IShellOutputReceiver)
      */
@@ -242,16 +242,25 @@
         AdbHelper.executeRemoteCommand(AndroidDebugBridge.sSocketAddr, command, this,
                 receiver);
     }
-    
-    /* 
+
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#runEventLogService(com.android.ddmlib.log.LogReceiver)
      */
     public void runEventLogService(LogReceiver receiver) throws IOException {
         AdbHelper.runEventLogService(AndroidDebugBridge.sSocketAddr, this, receiver);
     }
-    
-    /* 
+
+    /*
+     * (non-Javadoc)
+     * @see com.android.ddmlib.IDevice#runLogService(com.android.ddmlib.log.LogReceiver)
+     */
+    public void runLogService(String logname,
+            LogReceiver receiver) throws IOException {
+        AdbHelper.runLogService(AndroidDebugBridge.sSocketAddr, this, logname, receiver);
+    }
+
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#createForward(int, int)
      */
@@ -265,7 +274,7 @@
         }
     }
 
-    /* 
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#removeForward(int, int)
      */
@@ -279,7 +288,7 @@
         }
     }
 
-    /* 
+    /*
      * (non-Javadoc)
      * @see com.android.ddmlib.IDevice#getClientName(int)
      */
@@ -325,7 +334,7 @@
 
         return false;
     }
-    
+
     void clearClientList() {
         synchronized (mClients) {
             mClients.clear();
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
index 664b0c9..5dbce92 100755
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/IDevice.java
@@ -44,7 +44,7 @@
      * Returns the serial number of the device.
      */
     public String getSerialNumber();
-    
+
     /**
      * Returns the name of the AVD the emulator is running.
      * <p/>This is only valid if {@link #isEmulator()} returns true.
@@ -152,6 +152,14 @@
     public void runEventLogService(LogReceiver receiver) throws IOException;
 
     /**
+     * Runs the log service for the given log and outputs the log to the {@link LogReceiver}.
+     * @param logname the logname of the log to read from.
+     * @param receiver the receiver to receive the event log entries.
+     * @throws IOException
+     */
+    public void runLogService(String logname, LogReceiver receiver) throws IOException;
+
+    /**
      * Creates a port forwarding between a local and a remote port.
      * @param localPort the local port to forward
      * @param remotePort the remote port.
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/ITestRunListener.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/ITestRunListener.java
index 4c0d9de..b61a698 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/ITestRunListener.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/ITestRunListener.java
@@ -17,56 +17,69 @@
 package com.android.ddmlib.testrunner;
 
 /**
- * Listener for instrumentation test runs
- * 
- * Modeled after junit.runner.TestRunListener
+ * Receives event notifications during instrumentation test runs. 
+ * Patterned after {@link junit.runner.TestRunListener}.
  */
 public interface ITestRunListener {
-    public static final int STATUS_ERROR = 1;
-    public static final int STATUS_FAILURE = 2;
+
+    /**
+     *  Types of test failures.
+     */
+    enum TestFailure {
+        /** Test failed due to unanticipated uncaught exception. */
+        ERROR,
+        /** Test failed due to a false assertion. */
+        FAILURE
+    }
 
     /** 
-     * Reports the start of a test run 
-     * @param testCount - total number of tests in test run
-     * */
+     * Reports the start of a test run.
+     * 
+     * @param testCount total number of tests in test run
+     */
     public void testRunStarted(int testCount);
     
     /**
-     * Reports end of test run
-     * @param elapsedTime - device reported elapsed time, in milliseconds
+     * Reports end of test run.
+     * 
+     * @param elapsedTime device reported elapsed time, in milliseconds
      */
     public void testRunEnded(long elapsedTime);
 
     /**
-     * Reports test run stopped before completion
-     * @param elapsedTime - device reported elapsed time, in milliseconds
+     * Reports test run stopped before completion.
+     * 
+     * @param elapsedTime device reported elapsed time, in milliseconds
      */
     public void testRunStopped(long elapsedTime);
 
     /**
-     * Reports the start of an individual test case
-     */
-    public void testStarted(String className, String testName);
-
-    /**
-     * Reports the execution end of an individual test case
-     * If no testFailed has been reported, this is a passed test
-     */
-    public void testEnded(String className, String testName);
-
-    /**
-     * Reports the failure of a individual test case
-     * Will be called between testStarted and testEnded
+     * Reports the start of an individual test case.
      * 
-     * @param status - one of STATUS_ERROR, STATUS_FAILURE
-     * @param className - name of test class
-     * @param testName - name of test method
-     * @param trace - stack trace of failure
+     * @param test identifies the test
      */
-    public void testFailed(int status, String className, String testName, String trace);
+    public void testStarted(TestIdentifier test);
+
+    /**
+     * Reports the execution end of an individual test case.
+     * If {@link #testFailed} was not invoked, this test passed.
+     * 
+     * @param test identifies the test
+     */
+    public void testEnded(TestIdentifier test);
+
+    /**
+     * Reports the failure of a individual test case.
+     * Will be called between testStarted and testEnded.
+     * 
+     * @param status failure type
+     * @param test identifies the test
+     * @param trace stack trace of failure
+     */
+    public void testFailed(TestFailure status, TestIdentifier test, String trace);
     
     /** 
-     * Reports test run failed to execute due to a fatal error 
+     * Reports test run failed to execute due to a fatal error.
      */
     public void testRunFailed(String errorMessage);
 }
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java
index d47bd56..bc1834f 100755
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/InstrumentationResultParser.java
@@ -20,24 +20,21 @@
 import com.android.ddmlib.Log;
 import com.android.ddmlib.MultiLineReceiver;
 
-import java.util.Hashtable;
-import java.util.Map;
-
 /**
- * Parses the 'raw output mode' results of an instrument test run from shell, and informs a 
- * ITestRunListener of the results
+ * Parses the 'raw output mode' results of an instrumentation test run from shell and informs a 
+ * ITestRunListener of the results.
  * 
- * Expects the following output:
+ * <p>Expects the following output:
  * 
- * If fatal error occurred when attempted to run the tests:
- * <i> INSTRUMENTATION_FAILED: </i>  
+ * <p>If fatal error occurred when attempted to run the tests:
+ * <pre> INSTRUMENTATION_FAILED: </pre>  
  * 
- * Otherwise, expect a series of test results, each one containing a set of status key/value
+ * <p>Otherwise, expect a series of test results, each one containing a set of status key/value
  * pairs, delimited by a start(1)/pass(0)/fail(-2)/error(-1) status code result. At end of test 
  * run, expects that the elapsed test time in seconds will be displayed  
  * 
- * i.e.
- * <i>
+ * <p>For example:
+ * <pre>
  * INSTRUMENTATION_STATUS_CODE: 1
  * INSTRUMENTATION_STATUS: class=com.foo.FooTest
  * INSTRUMENTATION_STATUS: test=testFoo
@@ -48,64 +45,85 @@
  * ... 
  * 
  * Time: X
- * </i>
- * 
- * Note that the "value" portion of the key-value pair may wrap over several text lines
+ * </pre>
+ * <p>Note that the "value" portion of the key-value pair may wrap over several text lines
  */
 public class InstrumentationResultParser extends MultiLineReceiver {
     
-    // relevant test status keys
-    private static final String CODE_KEY = "code";
-    private static final String TEST_KEY = "test";
-    private static final String CLASS_KEY = "class";
-    private static final String STACK_KEY = "stack";
-    private static final String NUMTESTS_KEY = "numtests";
+    /** Relevant test status keys. */
+    private static class StatusKeys {
+        private static final String TEST = "test";
+        private static final String CLASS = "class";
+        private static final String STACK = "stack";
+        private static final String NUMTESTS = "numtests";
+    }
     
-    // test result status codes
-    private static final int FAILURE_STATUS_CODE = -2;
-    private static final int START_STATUS_CODE = 1;
-    private static final int ERROR_STATUS_CODE = -1;
-    private static final int OK_STATUS_CODE = 0;
+    /** Test result status codes. */
+    private static class StatusCodes {
+        private static final int FAILURE = -2;
+        private static final int START = 1;
+        private static final int ERROR = -1;
+        private static final int OK = 0;
+    }
 
-    // recognized output patterns
-    private static final String STATUS_PREFIX = "INSTRUMENTATION_STATUS: ";
-    private static final String STATUS_PREFIX_CODE = "INSTRUMENTATION_STATUS_CODE: ";
-    private static final String STATUS_FAILED = "INSTRUMENTATION_FAILED: ";
-    private static final String TIME_REPORT = "Time: ";
+    /** Prefixes used to identify output. */
+    private static class Prefixes {
+        private static final String STATUS = "INSTRUMENTATION_STATUS: ";
+        private static final String STATUS_CODE = "INSTRUMENTATION_STATUS_CODE: ";
+        private static final String STATUS_FAILED = "INSTRUMENTATION_FAILED: ";
+        private static final String TIME_REPORT = "Time: ";
+    }
     
     private final ITestRunListener mTestListener;
-    /** key-value map for current test */
-    private Map<String, String> mStatusValues;
-    /** stores the current "key" portion of the status key-value being parsed */
-    private String mCurrentKey;
-    /** stores the current "value" portion of the status key-value being parsed */
-    private StringBuilder mCurrentValue;
-    /** true if start of test has already been reported to listener */
-    private boolean mTestStartReported;
-    /** the elapsed time of the test run, in ms */
-    private long mTestTime;
-    /** true if current test run has been canceled by user */
-    private boolean mIsCancelled;
+
+    /** 
+     * Test result data
+     */
+    private static class TestResult {
+        private Integer mCode = null;
+        private String mTestName = null;
+        private String mTestClass = null;
+        private String mStackTrace = null;
+        private Integer mNumTests = null;
+        
+        /** Returns true if all expected values have been parsed */
+        boolean isComplete() {
+            return mCode != null && mTestName != null && mTestClass != null;
+        }
+    }
+    
+    /** Stores the status values for the test result currently being parsed */
+    private TestResult mCurrentTestResult = null;
+    
+    /** Stores the current "key" portion of the status key-value being parsed. */
+    private String mCurrentKey = null;
+    
+    /** Stores the current "value" portion of the status key-value being parsed. */
+    private StringBuilder mCurrentValue = null;
+    
+    /** True if start of test has already been reported to listener. */
+    private boolean mTestStartReported = false;
+    
+    /** The elapsed time of the test run, in milliseconds. */
+    private long mTestTime = 0;
+    
+    /** True if current test run has been canceled by user. */
+    private boolean mIsCancelled = false;
     
     private static final String LOG_TAG = "InstrumentationResultParser";
     
     /**
-     * Creates the InstrumentationResultParser
-     * @param listener - listener to report results to. will be informed of test results as the 
-     * tests are executing
+     * Creates the InstrumentationResultParser.
+     * 
+     * @param listener informed of test results as the tests are executing
      */
     public InstrumentationResultParser(ITestRunListener listener) {
-        mStatusValues = new Hashtable<String, String>();
-        mCurrentKey = null;
-        setTrimLine(false);
         mTestListener = listener;
-        mTestStartReported = false;
-        mTestTime = 0;
-        mIsCancelled = false;
     }
     
     /**
-     * Processes the instrumentation test output from shell
+     * Processes the instrumentation test output from shell.
+     * 
      * @see MultiLineReceiver#processNewLines
      */
     @Override
@@ -116,31 +134,37 @@
     }
     
     /**
-     * Parse an individual output line. Expects a line that either is:
-     * a) the start of a new status line (ie. starts with STATUS_PREFIX or STATUS_PREFIX_CODE), 
-     *    and thus there is a new key=value pair to parse, and the previous key-value pair is 
-     *    finished
-     * b) a continuation of the previous status (ie the "value" portion of the key has wrapped
-     *    to the next line. 
-     * c) a line reporting a fatal error in the test run (STATUS_FAILED)
-     * d) a line reporting the total elapsed time of the test run.  
+     * Parse an individual output line. Expects a line that is one of:
+     * <ul>
+     * <li> 
+     * The start of a new status line (starts with Prefixes.STATUS or Prefixes.STATUS_CODE), 
+     * and thus there is a new key=value pair to parse, and the previous key-value pair is 
+     * finished. 
+     * </li>
+     * <li>
+     * A continuation of the previous status (the "value" portion of the key has wrapped
+     * to the next line).
+     * </li>  
+     * <li> A line reporting a fatal error in the test run (Prefixes.STATUS_FAILED) </li>
+     * <li> A line reporting the total elapsed time of the test run. (Prefixes.TIME_REPORT) </li>  
+     * </ul>
      *    
-     * @param line - text output line
+     * @param line  Text output line
      */
     private void parse(String line) {
-        if (line.startsWith(STATUS_PREFIX_CODE)) {
+        if (line.startsWith(Prefixes.STATUS_CODE)) {
             // Previous status key-value has been collected. Store it.
             submitCurrentKeyValue();
             parseStatusCode(line);
-        } else if (line.startsWith(STATUS_PREFIX)) {
+        } else if (line.startsWith(Prefixes.STATUS)) {
             // Previous status key-value has been collected. Store it.
             submitCurrentKeyValue();
-            parseKey(line, STATUS_PREFIX.length());
-        } else if (line.startsWith(STATUS_FAILED)) {
+            parseKey(line, Prefixes.STATUS.length());
+        } else if (line.startsWith(Prefixes.STATUS_FAILED)) {
             Log.e(LOG_TAG, "test run failed " + line);
             mTestListener.testRunFailed(line);
-        } else if (line.startsWith(TIME_REPORT)) {
-            parseTime(line, TIME_REPORT.length());
+        } else if (line.startsWith(Prefixes.TIME_REPORT)) {
+            parseTime(line, Prefixes.TIME_REPORT.length());
         } else {
             if (mCurrentValue != null) {
                 // this is a value that has wrapped to next line. 
@@ -153,21 +177,53 @@
     }
     
     /**
-     * Stores the currently parsed key-value pair in the status map
+     * Stores the currently parsed key-value pair into mCurrentTestInfo.
      */
     private void submitCurrentKeyValue() {
         if (mCurrentKey != null && mCurrentValue != null) {
-            mStatusValues.put(mCurrentKey, mCurrentValue.toString());
+            TestResult testInfo = getCurrentTestInfo();
+            String statusValue = mCurrentValue.toString();
+
+            if (mCurrentKey.equals(StatusKeys.CLASS)) {
+                testInfo.mTestClass = statusValue.trim();
+            }
+            else if (mCurrentKey.equals(StatusKeys.TEST)) {
+                testInfo.mTestName = statusValue.trim();
+            }
+            else if (mCurrentKey.equals(StatusKeys.NUMTESTS)) {
+                try {
+                    testInfo.mNumTests = Integer.parseInt(statusValue);
+                }
+                catch (NumberFormatException e) {
+                    Log.e(LOG_TAG, "Unexpected integer number of tests, received " + statusValue);
+                }
+            }
+            else if (mCurrentKey.equals(StatusKeys.STACK)) {
+                testInfo.mStackTrace = statusValue;
+            }
+
             mCurrentKey = null;
             mCurrentValue = null;
         }
     }
     
+    private TestResult getCurrentTestInfo() {
+        if (mCurrentTestResult == null) {
+            mCurrentTestResult = new TestResult();
+        }
+        return mCurrentTestResult;
+    }
+    
+    private void clearCurrentTestInfo() {
+        mCurrentTestResult = null;
+    }
+    
     /**
-     * Parses the key from the current line
-     * Expects format of "key=value",  
-     * @param line - full line of text to parse 
-     * @param keyStartPos - the starting position of the key in the given line
+     * Parses the key from the current line.
+     * Expects format of "key=value".
+     *  
+     * @param line full line of text to parse 
+     * @param keyStartPos the starting position of the key in the given line
      */
     private void parseKey(String line, int keyStartPos) {
         int endKeyPos = line.indexOf('=', keyStartPos);
@@ -178,7 +234,8 @@
     }
     
     /**
-     * Parses the start of a key=value pair. 
+     * Parses the start of a key=value pair.
+     *  
      * @param line - full line of text to parse 
      * @param valueStartPos - the starting position of the value in the given line
      */
@@ -188,20 +245,25 @@
     }
     
     /**
-     * Parses out a status code result. For consistency, stores the result as a CODE entry in 
-     * key-value status map 
+     * Parses out a status code result. 
      */
     private void parseStatusCode(String line) {
-        String value = line.substring(STATUS_PREFIX_CODE.length()).trim();
-        mStatusValues.put(CODE_KEY, value);
+        String value = line.substring(Prefixes.STATUS_CODE.length()).trim();
+        TestResult testInfo = getCurrentTestInfo();
+        try {
+            testInfo.mCode = Integer.parseInt(value);    
+        }
+        catch (NumberFormatException e) {
+            Log.e(LOG_TAG, "Expected integer status code, received: " + value);
+        }
         
         // this means we're done with current test result bundle
-        reportResult(mStatusValues);
-        mStatusValues.clear();
+        reportResult(testInfo);
+        clearCurrentTestInfo();
     }
     
     /**
-     * Returns true if test run canceled
+     * Returns true if test run canceled.
      * 
      * @see IShellOutputReceiver#isCancelled()
      */
@@ -210,7 +272,7 @@
     }
     
     /**
-     * Requests cancellation of test result parsing
+     * Requests cancellation of test run.
      */
     public void cancel() {
         mIsCancelled = true;
@@ -219,82 +281,62 @@
     /**
      * Reports a test result to the test run listener. Must be called when a individual test
      * result has been fully parsed. 
-     * @param statusMap - key-value status pairs of test result
+     * 
+     * @param statusMap key-value status pairs of test result
      */
-    private void reportResult(Map<String, String> statusMap) {
-        String className = statusMap.get(CLASS_KEY);
-        String testName = statusMap.get(TEST_KEY);
-        String statusCodeString = statusMap.get(CODE_KEY);
-        
-        if (className == null || testName == null || statusCodeString == null) {
-            Log.e(LOG_TAG, "invalid instrumentation status bundle " + statusMap.toString());
+    private void reportResult(TestResult testInfo) {
+        if (!testInfo.isComplete()) {
+            Log.e(LOG_TAG, "invalid instrumentation status bundle " + testInfo.toString());
             return;
         }
-        className = className.trim();
-        testName = testName.trim();
+        reportTestRunStarted(testInfo);
+        TestIdentifier testId = new TestIdentifier(testInfo.mTestClass, testInfo.mTestName);
 
-        reportTestStarted(statusMap);
-        
-        try {
-           int statusCode = Integer.parseInt(statusCodeString);
-           
-           switch (statusCode) {
-               case START_STATUS_CODE:
-                   mTestListener.testStarted(className, testName);
-                   break;
-               case FAILURE_STATUS_CODE:
-                   mTestListener.testFailed(ITestRunListener.STATUS_FAILURE, className, testName, 
-                           getTrace(statusMap));
-                   mTestListener.testEnded(className, testName);
-                   break;
-               case ERROR_STATUS_CODE:
-                   mTestListener.testFailed(ITestRunListener.STATUS_ERROR, className, testName, 
-                           getTrace(statusMap));
-                   mTestListener.testEnded(className, testName);
-                   break;
-               case OK_STATUS_CODE:
-                   mTestListener.testEnded(className, testName);
-                   break;
-               default:
-                   Log.e(LOG_TAG, "Expected status code, received: " + statusCodeString);
-                   mTestListener.testEnded(className, testName);
-                   break;
-           }
+        switch (testInfo.mCode) {
+            case StatusCodes.START:
+                mTestListener.testStarted(testId);
+                break;
+            case StatusCodes.FAILURE:
+                mTestListener.testFailed(ITestRunListener.TestFailure.FAILURE, testId, 
+                        getTrace(testInfo));
+                mTestListener.testEnded(testId);
+                break;
+            case StatusCodes.ERROR:
+                mTestListener.testFailed(ITestRunListener.TestFailure.ERROR, testId, 
+                        getTrace(testInfo));
+                mTestListener.testEnded(testId);
+                break;
+            case StatusCodes.OK:
+                mTestListener.testEnded(testId);
+                break;
+            default:
+                Log.e(LOG_TAG, "Unknown status code received: " + testInfo.mCode);
+                mTestListener.testEnded(testId);
+            break;
         }
-        catch (NumberFormatException e) {
-           Log.e(LOG_TAG, "Expected integer status code, received: " + statusCodeString);    
-        }
+
     }
     
     /**
      * Reports the start of a test run, and the total test count, if it has not been previously 
-     * reported
-     * @param statusMap - key-value status pairs
+     * reported.
+     * 
+     * @param testInfo current test status values
      */
-    private void reportTestStarted(Map<String, String> statusMap) {
+    private void reportTestRunStarted(TestResult testInfo) {
         // if start test run not reported yet
-        if (!mTestStartReported) {
-            String numTestsString = statusMap.get(NUMTESTS_KEY);
-            if (numTestsString != null) {
-                try {
-                    int numTests = Integer.parseInt(numTestsString);
-                    mTestListener.testRunStarted(numTests);
-                    mTestStartReported = true;
-                }
-                catch (NumberFormatException e) {
-                    Log.e(LOG_TAG, "Unexpected numTests format " + numTestsString);
-                }
-            }
+        if (!mTestStartReported && testInfo.mNumTests != null) {
+            mTestListener.testRunStarted(testInfo.mNumTests);
+            mTestStartReported = true;
         }
     }
     
     /**
-     * Returns the stack trace of the current failed test, from the provided key-value status map
+     * Returns the stack trace of the current failed test, from the provided testInfo.
      */
-    private String getTrace(Map<String, String> statusMap) {
-        String stackTrace = statusMap.get(STACK_KEY);
-        if (stackTrace != null) {        
-            return stackTrace;    
+    private String getTrace(TestResult testInfo) {
+        if (testInfo.mStackTrace != null) {
+            return testInfo.mStackTrace;    
         }
         else {
             Log.e(LOG_TAG, "Could not find stack trace for failed test ");
@@ -303,7 +345,7 @@
     }
     
     /**
-     * Parses out and store the elapsed time
+     * Parses out and store the elapsed time.
      */
     private void parseTime(String line, int startPos) {
         String timeString = line.substring(startPos);
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
index 5de632e..4edbbbb 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunner.java
@@ -23,7 +23,7 @@
 import java.io.IOException;
 
 /**
- * Runs a Android test command remotely and reports results
+ * Runs a Android test command remotely and reports results.
  */
 public class RemoteAndroidTestRunner  {
 
@@ -43,11 +43,12 @@
         "android.test.InstrumentationTestRunner";
     
     /**
-     * Creates a remote android test runner.
-     * @param packageName - the Android application package that contains the tests to run 
-     * @param runnerName - the instrumentation test runner to execute. If null, will use default
+     * Creates a remote Android test runner.
+     * 
+     * @param packageName the Android application package that contains the tests to run 
+     * @param runnerName the instrumentation test runner to execute. If null, will use default
      *   runner 
-     * @param remoteDevice - the Android device to execute tests on
+     * @param remoteDevice the Android device to execute tests on
      */
     public RemoteAndroidTestRunner(String packageName, 
                                    String runnerName,
@@ -62,9 +63,10 @@
     }
     
     /**
-     * Alternate constructor. Uses default instrumentation runner
-     * @param packageName - the Android application package that contains the tests to run 
-     * @param remoteDevice - the Android device to execute tests on
+     * Alternate constructor. Uses default instrumentation runner.
+     * 
+     * @param packageName the Android application package that contains the tests to run 
+     * @param remoteDevice the Android device to execute tests on
      */
     public RemoteAndroidTestRunner(String packageName, 
                                    IDevice remoteDevice) {
@@ -72,14 +74,14 @@
     }
     
     /**
-     * Returns the application package name
+     * Returns the application package name.
      */
     public String getPackageName() {
         return mPackageName;
     }
 
     /**
-     * Returns the runnerName
+     * Returns the runnerName.
      */
     public String getRunnerName() {
         if (mRunnerName == null) {
@@ -89,7 +91,7 @@
     }
     
     /**
-     * Returns the complete instrumentation component path 
+     * Returns the complete instrumentation component path.
      */
     private String getRunnerPath() {
         return getPackageName() + RUNNER_SEPARATOR + getRunnerName();
@@ -97,8 +99,9 @@
     
     /**
      * Sets to run only tests in this class
-     * Must be called before 'run'
-     * @param className - fully qualified class name (eg x.y.z)
+     * Must be called before 'run'.
+     * 
+     * @param className fully qualified class name (eg x.y.z)
      */
     public void setClassName(String className) {
         mClassArg = className;
@@ -106,10 +109,12 @@
 
     /**
      * Sets to run only tests in the provided classes
-     * Must be called before 'run'
+     * Must be called before 'run'.
+     * <p>
      * If providing more than one class, requires a InstrumentationTestRunner that supports 
-     * the multiple class argument syntax 
-     * @param classNames - array of fully qualified class name (eg x.y.z)
+     * the multiple class argument syntax.
+     * 
+     * @param classNames array of fully qualified class names (eg x.y.z)
      */
     public void setClassNames(String[] classNames) {
         StringBuilder classArgBuilder = new StringBuilder();
@@ -125,9 +130,10 @@
     
     /**
      * Sets to run only specified test method
-     * Must be called before 'run'
-     * @param className - fully qualified class name (eg x.y.z)
-     * @param testName - method name
+     * Must be called before 'run'.
+     * 
+     * @param className fully qualified class name (eg x.y.z)
+     * @param testName method name
      */
     public void setMethodName(String className, String testName) {
         mClassArg = className + METHOD_SEPARATOR + testName;
@@ -135,8 +141,9 @@
     
     /**
      * Sets extra arguments to include in instrumentation command.
-     * Must be called before 'run'
-     * @param instrumentationArgs - must not be null
+     * Must be called before 'run'.
+     * 
+     * @param instrumentationArgs must not be null
      */
     public void setExtraArgs(String instrumentationArgs) {
         if (instrumentationArgs == null) {
@@ -146,23 +153,23 @@
     }
     
     /**
-     * Returns the extra instrumentation arguments
+     * Returns the extra instrumentation arguments.
      */
     public String getExtraArgs() {
         return mExtraArgs;
     }
     
     /**
-     * Sets this test run to log only mode - skips test execution
+     * Sets this test run to log only mode - skips test execution.
      */
     public void setLogOnly(boolean logOnly) {
         mLogOnlyMode = logOnly;
     }
     
     /**
-     * Execute this test run
+     * Execute this test run.
      * 
-     * @param listener - listener to report results to
+     * @param listener listens for test results
      */
     public void run(ITestRunListener listener) {
         final String runCaseCommandStr = "am instrument -w -r "
@@ -179,7 +186,7 @@
     }
     
     /**
-     * Requests cancellation of this test run
+     * Requests cancellation of this test run.
      */
     public void cancel() {
         if (mParser != null) {
@@ -188,7 +195,7 @@
     }
     
     /**
-     * Returns the test class argument
+     * Returns the test class argument.
      */
     private String getClassArg() {
         return mClassArg;
@@ -196,7 +203,7 @@
     
     /**
      * Returns the full instrumentation command which specifies the test classes to execute. 
-     * Returns an empty string if no classes were specified
+     * Returns an empty string if no classes were specified.
      */
     private String getClassCmd() {
         String classArg = getClassArg();
@@ -208,7 +215,7 @@
 
     /**
      * Returns the full command to enable log only mode - if specified. Otherwise returns an 
-     * empty string
+     * empty string.
      */
     private String getLogCmd() {
         if (mLogOnlyMode) {
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/TestIdentifier.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/TestIdentifier.java
new file mode 100644
index 0000000..4d3b108
--- /dev/null
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/testrunner/TestIdentifier.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008 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.ddmlib.testrunner;
+
+/**
+ * Identifies a parsed instrumentation test 
+ */
+public class TestIdentifier {
+
+    private final String mClassName;
+    private final String mTestName;
+    
+    /**
+     * Creates a test identifier
+     * 
+     * @param className fully qualified class name of the test. Cannot be null.
+     * @param testName name of the test. Cannot be null.
+     */
+    public TestIdentifier(String className, String testName) {
+        if (className == null || testName == null) {
+            throw new IllegalArgumentException("className and testName must " + 
+                    "be non-null");
+        }
+        mClassName = className;
+        mTestName = testName;
+    }
+    
+    /**
+     * Returns the fully qualified class name of the test
+     */
+    public String getClassName() {
+        return mClassName;
+    }
+
+    /**
+     * Returns the name of the test
+     */
+    public String getTestName() {
+        return mTestName;
+    }
+    
+    /**
+     * Tests equality by comparing class and method name
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof TestIdentifier)) {
+            return false;
+        }
+        TestIdentifier otherTest = (TestIdentifier)other;
+        return getClassName().equals(otherTest.getClassName())  && 
+                getTestName().equals(otherTest.getTestName());
+    }
+    
+    /**
+     * Generates hashCode based on class and method name.
+     */
+    @Override
+    public int hashCode() {
+        return getClassName().hashCode() * 31 + getTestName().hashCode();
+    }
+}
diff --git a/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java b/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java
index 67f6198..77d10c1 100644
--- a/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java
+++ b/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/InstrumentationResultParserTest.java
@@ -20,7 +20,7 @@
 
 
 /**
- * Tests InstrumentationResultParser
+ * Tests InstrumentationResultParser.
  */
 public class InstrumentationResultParserTest extends TestCase {
 
@@ -51,7 +51,7 @@
 
     /**
      * Tests that the test run started and test start events is sent on first
-     * bundle received
+     * bundle received.
      */
     public void testTestStarted() {
         StringBuilder output = buildCommonResult();
@@ -63,7 +63,7 @@
     }
 
     /**
-     * Tests that a single successful test execution
+     * Tests that a single successful test execution.
      */
     public void testTestSuccess() {
         StringBuilder output = buildCommonResult();
@@ -74,11 +74,11 @@
         injectTestString(output.toString());
         assertCommonAttributes();
         assertEquals(1, mTestResult.mNumTestsRun);
-        assertEquals(0, mTestResult.mTestStatus);
+        assertEquals(null, mTestResult.mTestStatus);
     }
 
     /**
-     * Test basic parsing of failed test case
+     * Test basic parsing of failed test case.
      */
     public void testTestFailed() {
         StringBuilder output = buildCommonResult();
@@ -91,12 +91,12 @@
         assertCommonAttributes();
 
         assertEquals(1, mTestResult.mNumTestsRun);
-        assertEquals(ITestRunListener.STATUS_FAILURE, mTestResult.mTestStatus);
+        assertEquals(ITestRunListener.TestFailure.FAILURE, mTestResult.mTestStatus);
         assertEquals(STACK_TRACE, mTestResult.mTrace);
     }
     
     /**
-     * Test basic parsing and conversion of time from output
+     * Test basic parsing and conversion of time from output.
      */
     public void testTimeParsing() {
         final String timeString = "Time: 4.9";
@@ -105,7 +105,7 @@
     }
 
     /**
-     * builds a common test result using TEST_NAME and TEST_CLASS
+     * builds a common test result using TEST_NAME and TEST_CLASS.
      */
     private StringBuilder buildCommonResult() {
         StringBuilder output = new StringBuilder();
@@ -118,7 +118,7 @@
     }
 
     /**
-     * Adds common status results to the provided output
+     * Adds common status results to the provided output.
      */
     private void addCommonStatus(StringBuilder output) {
         addStatusKey(output, "stream", "\r\n" + CLASS_NAME);
@@ -130,7 +130,7 @@
     }
 
     /**
-     * Adds a stack trace status bundle to output
+     * Adds a stack trace status bundle to output.
      */
     private void addStackTrace(StringBuilder output) {
         addStatusKey(output, "stack", STACK_TRACE);
@@ -138,7 +138,7 @@
     }
 
     /**
-     * Helper method to add a status key-value bundle
+     * Helper method to add a status key-value bundle.
      */
     private void addStatusKey(StringBuilder outputBuilder, String key,
             String value) {
@@ -168,7 +168,7 @@
     }
 
     /**
-     * inject a test string into the result parser
+     * inject a test string into the result parser.
      * 
      * @param result
      */
@@ -185,7 +185,7 @@
     }
 
     /**
-     * A specialized test listener that stores a single test events
+     * A specialized test listener that stores a single test events.
      */
     private class VerifyingTestResult implements ITestRunListener {
 
@@ -194,29 +194,28 @@
         int mNumTestsRun;
         String mTestName;
         long mTestTime;
-        int mTestStatus;
+        TestFailure mTestStatus;
         String mTrace;
         boolean mStopped;
 
         VerifyingTestResult() {
             mNumTestsRun = 0;
-            mTestStatus = 0;
+            mTestStatus = null;
             mStopped = false;
         }
 
-        public void testEnded(String className, String testName) {
+        public void testEnded(TestIdentifier test) {
             mNumTestsRun++;
-            assertEquals("Unexpected class name", mSuiteName, className);
-            assertEquals("Unexpected test ended", mTestName, testName);
+            assertEquals("Unexpected class name", mSuiteName, test.getClassName());
+            assertEquals("Unexpected test ended", mTestName, test.getTestName());
 
         }
 
-        public void testFailed(int status, String className, String testName,
-                String trace) {
+        public void testFailed(TestFailure status, TestIdentifier test, String trace) {
             mTestStatus = status;
             mTrace = trace;
-            assertEquals("Unexpected class name", mSuiteName, className);
-            assertEquals("Unexpected test ended", mTestName, testName);
+            assertEquals("Unexpected class name", mSuiteName, test.getClassName());
+            assertEquals("Unexpected test ended", mTestName, test.getTestName());
         }
 
         public void testRunEnded(long elapsedTime) {
@@ -233,9 +232,9 @@
             mStopped = true;
         }
 
-        public void testStarted(String className, String testName) {
-            mSuiteName = className;
-            mTestName = testName;
+        public void testStarted(TestIdentifier test) {
+            mSuiteName = test.getClassName();
+            mTestName = test.getTestName();
         }
 
         public void testRunFailed(String errorMessage) {
diff --git a/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java b/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
index 54ffae8..9acaaf9 100644
--- a/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
+++ b/tools/ddms/libs/ddmlib/tests/src/com/android/ddmlib/testrunner/RemoteAndroidTestRunnerTest.java
@@ -31,16 +31,16 @@
 import java.util.Map;
 
 /**
- * Test RemoteAndroidTestRunner. 
+ * Tests RemoteAndroidTestRunner.
  */
 public class RemoteAndroidTestRunnerTest extends TestCase {
 
     private RemoteAndroidTestRunner mRunner;
     private MockDevice mMockDevice;
-    
+
     private static final String TEST_PACKAGE = "com.test";
     private static final String TEST_RUNNER = "com.test.InstrumentationTestRunner";
-    
+
     /**
      * @see junit.framework.TestCase#setUp()
      */
@@ -49,40 +49,40 @@
         mMockDevice = new MockDevice();
         mRunner = new RemoteAndroidTestRunner(TEST_PACKAGE, TEST_RUNNER, mMockDevice);
     }
-    
+
     /**
-     * Test the basic case building of the instrumentation runner command with no arguments
+     * Test the basic case building of the instrumentation runner command with no arguments.
      */
     public void testRun() {
         mRunner.run(new EmptyListener());
-        assertStringsEquals(String.format("am instrument -w -r %s/%s", TEST_PACKAGE, TEST_RUNNER), 
+        assertStringsEquals(String.format("am instrument -w -r %s/%s", TEST_PACKAGE, TEST_RUNNER),
                 mMockDevice.getLastShellCommand());
     }
 
     /**
-     * Test the building of the instrumentation runner command with log set
+     * Test the building of the instrumentation runner command with log set.
      */
     public void testRunWithLog() {
         mRunner.setLogOnly(true);
         mRunner.run(new EmptyListener());
-        assertStringsEquals(String.format("am instrument -w -r -e log true %s/%s", TEST_PACKAGE, 
+        assertStringsEquals(String.format("am instrument -w -r -e log true %s/%s", TEST_PACKAGE,
                 TEST_RUNNER), mMockDevice.getLastShellCommand());
     }
 
     /**
-     * Test the building of the instrumentation runner command with method set
+     * Test the building of the instrumentation runner command with method set.
      */
     public void testRunWithMethod() {
         final String className = "FooTest";
         final String testName = "fooTest";
         mRunner.setMethodName(className, testName);
         mRunner.run(new EmptyListener());
-        assertStringsEquals(String.format("am instrument -w -r -e class %s#%s %s/%s", className, 
+        assertStringsEquals(String.format("am instrument -w -r -e class %s#%s %s/%s", className,
                 testName, TEST_PACKAGE, TEST_RUNNER), mMockDevice.getLastShellCommand());
     }
-    
+
     /**
-     * Test the building of the instrumentation runner command with extra args set
+     * Test the building of the instrumentation runner command with extra args set.
      */
     public void testRunWithExtraArgs() {
         final String extraArgs = "blah";
@@ -94,37 +94,37 @@
 
 
     /**
-     * Assert two strings are equal ignoring whitespace
+     * Assert two strings are equal ignoring whitespace.
      */
     private void assertStringsEquals(String str1, String str2) {
         String strippedStr1 = str1.replaceAll(" ", "");
         String strippedStr2 = str2.replaceAll(" ", "");
         assertEquals(strippedStr1, strippedStr2);
     }
-    
+
     /**
-     * A dummy device that does nothing except store the provided executed shell command for 
-     * later retrieval
+     * A dummy device that does nothing except store the provided executed shell command for
+     * later retrieval.
      */
     private static class MockDevice implements IDevice {
 
         private String mLastShellCommand;
-        
+
         /**
-         * Stores the provided command for later retrieval from getLastShellCommand
+         * Stores the provided command for later retrieval from getLastShellCommand.
          */
         public void executeShellCommand(String command,
                 IShellOutputReceiver receiver) throws IOException {
             mLastShellCommand = command;
         }
-        
+
         /**
-         * Get the last command provided to executeShellCommand
+         * Get the last command provided to executeShellCommand.
          */
         public String getLastShellCommand() {
             return mLastShellCommand;
         }
-        
+
         public boolean createForward(int localPort, int remotePort) {
             throw new UnsupportedOperationException();
         }
@@ -201,22 +201,26 @@
             throw new UnsupportedOperationException();
         }
 
+        public void runLogService(String logname, LogReceiver receiver) throws IOException {
+            throw new UnsupportedOperationException();
+        }
+
         public String getAvdName() {
             return "";
         }
 
     }
-    
-    /** An empty implementation of TestRunListener
+
+    /**
+     * An empty implementation of ITestRunListener.
      */
     private static class EmptyListener implements ITestRunListener {
 
-        public void testEnded(String className, String testName) {
+        public void testEnded(TestIdentifier test) {
             // ignore
         }
 
-        public void testFailed(int status, String className, String testName,
-                String trace) {
+        public void testFailed(TestFailure status, TestIdentifier test, String trace) {
             // ignore
         }
 
@@ -236,9 +240,9 @@
             // ignore
         }
 
-        public void testStarted(String className, String testName) {
+        public void testStarted(TestIdentifier test) {
             // ignore
         }
-        
+
     }
 }
diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java
index 149d689..46461bf 100644
--- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java
+++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/NativeHeapPanel.java
@@ -237,7 +237,7 @@
      */
     private HashMap<Long, NativeStackCallInfo> mSourceCache =
         new HashMap<Long,NativeStackCallInfo>();
-    private int mTotalSize;
+    private long mTotalSize;
     private Button mSaveButton;
     private Button mSymbolsButton;
 
diff --git a/tools/draw9patch/etc/draw9patch.bat b/tools/draw9patch/etc/draw9patch.bat
index 1d56d85..e267b06 100755
--- a/tools/draw9patch/etc/draw9patch.bat
+++ b/tools/draw9patch/etc/draw9patch.bat
@@ -20,9 +20,9 @@
 rem and set up progdir to be the fully-qualified pathname of its directory.
 set prog=%~f0
 
-rem Change current directory to where ddms is, to avoid issues with directories
-rem containing whitespaces.
-cd %~dp0
+rem Change current directory and drive to where the script is, to avoid
+rem issues with directories containing whitespaces.
+cd /d %~dp0
 
 set jarfile=draw9patch.jar
 set frameworkdir=
diff --git a/tools/eclipse/changes.txt b/tools/eclipse/changes.txt
index 5cb8245..781930c 100644
--- a/tools/eclipse/changes.txt
+++ b/tools/eclipse/changes.txt
@@ -1,14 +1,18 @@
 0.9.0 (work in progress)
-- Support for SDK with multiple versions of the Android platform and vendor supplied add-ons.
+- Support for the new Android SDK with support for multiple versions of the Android platform and for vendor supplied add-ons.
+    * New Project Wizard lets you choose which platform/add-on to target.
+    * Project properties (right click project in Package Explorer, then "Properties"), lets you edit project target.
+    * New Launch configuration option to choose debug deployment target.
+- Ability to export multiple apk from one project, using resource filters. See the 'android' property for Android projects.
 
 0.8.1:
 
-- Alternate Layout wizard. In the layout editor, the "create" button is now enabled, and allows to easily create alternate versions.
+- Alternate Layout wizard. In the layout editor, the "create" button is now enabled to easily create alternate versions of the current layout.
 - Fixed issue with custom themes/styles in the layout editor.
-- Export Wizard: To export an application for release, sign with a non debug key. Accessible from the export menu, from the Android Tools contextual menu, or from the overview page of the manifest editor.
+- Export Wizard: To export an application for release, and sign it with a non debug key. Accessible from the export menu, from the Android Tools contextual menu, or from the overview page of the manifest editor.
 - New XML File Wizard: To easily create new XML resources file in the /res directory.
 - New checks on launch when attempting to debug on a device.
-- Basic support for drag'n'drop in Graphical layout editor. You can add new items by drag'n'drop from the palette. There's is no support for moving/resizing yet.
+- Basic support for drag'n'drop in Graphical layout editor. You can add new items by drag'n'drop from the palette. There is no support for moving/resizing yet.
 - Undo/redo support in all XML form editors and Graphical layout editor.
 
 0.8.0:
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/new_adt_project.png b/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/new_adt_project.png
new file mode 100644
index 0000000..0f0e883
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/new_adt_project.png
Binary files differ
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/new_xml.png b/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/new_xml.png
new file mode 100644
index 0000000..8273185
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/icons/new_xml.png
Binary files differ
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
index f7c366d..d6c9ac1 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/plugin.xml
@@ -18,6 +18,14 @@
       <persistent value="true"/>
    </extension>
    <extension
+         id="com.android.ide.eclipse.common.aapt2Problem"
+         name="Android AAPT Problem"
+         point="org.eclipse.core.resources.markers">
+      <super type="org.eclipse.core.resources.problemmarker"/>
+      <super type="org.eclipse.core.resources.textmarker"/>
+      <persistent value="true"/>
+   </extension>
+   <extension
          id="com.android.ide.eclipse.common.aidlProblem"
          name="Android AIDL Problem"
          point="org.eclipse.core.resources.markers">
@@ -464,4 +472,31 @@
          </enabledWhen>
       </page>
    </extension>
+   <extension
+         point="org.eclipse.ui.actionSets">
+      <actionSet
+            description="Android Wizards"
+            id="adt.actionSet1"
+            label="Android Wizards"
+            visible="true">
+         <action
+               class="com.android.ide.eclipse.adt.wizards.actions.NewProjectAction"
+               icon="icons/new_adt_project.png"
+               id="com.android.ide.eclipse.adt.wizards.actions.NewProjectAction"
+               label="New Android Project"
+               style="push"
+               toolbarPath="android_project"
+               tooltip="Opens a wizard to help create a new Android project">
+         </action>
+         <action
+               class="com.android.ide.eclipse.adt.wizards.actions.NewXmlFileAction"
+               icon="icons/new_xml.png"
+               id="com.android.ide.eclipse.adt.wizards.actions.NewXmlFileAction"
+               label="New Android XML File"
+               style="push"
+               toolbarPath="android_project"
+               tooltip="Opens a wizard to help create a new Android XML file">
+         </action>
+      </actionSet>
+   </extension>
 </plugin>
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
index 9aa9354..61be3e5 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java
@@ -28,6 +28,7 @@
 import com.android.ide.eclipse.adt.sdk.AndroidTargetParser;
 import com.android.ide.eclipse.adt.sdk.LoadStatus;
 import com.android.ide.eclipse.adt.sdk.Sdk;
+import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
 import com.android.ide.eclipse.common.AndroidConstants;
 import com.android.ide.eclipse.common.EclipseUiHelper;
 import com.android.ide.eclipse.common.SdkStatsHelper;
@@ -173,7 +174,8 @@
     private final ArrayList<IJavaProject> mPostLoadProjectsToCheck = new ArrayList<IJavaProject>();
     
     private ResourceMonitor mResourceMonitor;
-    private ArrayList<Runnable> mResourceRefreshListener = new ArrayList<Runnable>();
+    private ArrayList<ITargetChangeListener> mTargetChangeListeners =
+            new ArrayList<ITargetChangeListener>();
 
     /**
      * Custom PrintStream for Dx output. This class overrides the method
@@ -861,7 +863,6 @@
     /**
      * Returns the lock object for SDK loading. If you wish to do things while the SDK is loading,
      * you must synchronize on this object.
-     * @return
      */
     public final Object getSdkLockObject() {
         return mPostLoadProjectsToResolve;
@@ -986,7 +987,7 @@
                             Constants.BUNDLE_VERSION);
                     Version version = new Version(versionString);
                     
-                    SdkStatsHelper.pingUsageServer("editors", version); //$NON-NLS-1$
+                    SdkStatsHelper.pingUsageServer("adt", version); //$NON-NLS-1$
                     
                     return Status.OK_STATUS;
                 } catch (Throwable t) {
@@ -1019,24 +1020,27 @@
                         
                         progress.setTaskName(Messages.AdtPlugin_Parsing_Resources);
                         
-                        for (IAndroidTarget target : sdk.getTargets()) {
-                            IStatus status = new AndroidTargetParser(target).run(progress);
-                            if (status.getCode() != IStatus.OK) {
-                                synchronized (getSdkLockObject()) {
-                                    mSdkIsLoaded = LoadStatus.FAILED;
-                                    mPostLoadProjectsToResolve.clear();
+                        int n = sdk.getTargets().length;
+                        if (n > 0) {
+                            int w = 60 / n;
+                            for (IAndroidTarget target : sdk.getTargets()) {
+                                SubMonitor p2 = progress.newChild(w);
+                                IStatus status = new AndroidTargetParser(target).run(p2);
+                                if (status.getCode() != IStatus.OK) {
+                                    synchronized (getSdkLockObject()) {
+                                        mSdkIsLoaded = LoadStatus.FAILED;
+                                        mPostLoadProjectsToResolve.clear();
+                                    }
+                                    return status;
                                 }
-                                return status;
                             }
                         }
 
-                        // FIXME: move this per platform, or somewhere else.
-                        progress = SubMonitor.convert(monitor,
-                                Messages.AdtPlugin_Parsing_Resources, 20);
-
                         synchronized (getSdkLockObject()) {
                             mSdkIsLoaded = LoadStatus.LOADED;
 
+                            progress.setTaskName("Check Projects");
+
                             // check the projects that need checking.
                             // The method modifies the list (it removes the project that
                             // do not need to be resolved again).
@@ -1052,25 +1056,33 @@
                                 AndroidClasspathContainerInitializer.updateProjects(array);
                                 mPostLoadProjectsToResolve.clear();
                             }
+                            
+                            progress.worked(10);
                         }
                     }
                         
                     // Notify resource changed listeners
-                    progress.subTask("Refresh UI");
-                    progress.setWorkRemaining(mResourceRefreshListener.size());
+                    progress.setTaskName("Refresh UI");
+                    progress.setWorkRemaining(mTargetChangeListeners.size());
                     
                     // Clone the list before iterating, to avoid Concurrent Modification
                     // exceptions
-                    List<Runnable> listeners = (List<Runnable>)mResourceRefreshListener.clone();
-                    for (Runnable listener : listeners) {
-                        try {
-                            AdtPlugin.getDisplay().syncExec(listener);
-                        } catch (Exception e) {
-                            AdtPlugin.log(e, "ResourceRefreshListener Failed");  //$NON-NLS-1$
-                        } finally {
-                            progress.worked(1);
+                    final List<ITargetChangeListener> listeners =
+                            (List<ITargetChangeListener>)mTargetChangeListeners.clone();
+                    final SubMonitor progress2 = progress;
+                    AdtPlugin.getDisplay().syncExec(new Runnable() {
+                        public void run() {
+                            for (ITargetChangeListener listener : listeners) {
+                                try {
+                                    listener.onTargetsLoaded();
+                                } catch (Exception e) {
+                                    AdtPlugin.log(e, "Failed to update a TargetChangeListener.");  //$NON-NLS-1$
+                                } finally {
+                                    progress2.worked(1);
+                                }
+                            }
                         }
-                    }
+                    });
                 } finally {
                     if (monitor != null) {
                         monitor.done();
@@ -1316,12 +1328,42 @@
         }, IResourceDelta.ADDED | IResourceDelta.CHANGED);
     }
 
-    public void addResourceChangedListener(Runnable resourceRefreshListener) {
-        mResourceRefreshListener.add(resourceRefreshListener);
+    /**
+     * Adds a new {@link ITargetChangeListener} to be notified when a new SDK is loaded, or when
+     * a project has its target changed.
+     */
+    public void addTargetListener(ITargetChangeListener listener) {
+        mTargetChangeListeners.add(listener);
     }
 
-    public void removeResourceChangedListener(Runnable resourceRefreshListener) {
-        mResourceRefreshListener.remove(resourceRefreshListener);
+    /**
+     * Removes an existing {@link ITargetChangeListener}.
+     * @see #addTargetListener(ITargetChangeListener)
+     */
+    public void removeTargetListener(ITargetChangeListener listener) {
+        mTargetChangeListeners.remove(listener);
+    }
+
+    /**
+     * Updates all the {@link ITargetChangeListener} that a target has changed for a given project.
+     * <p/>Only editors related to that project should reload.
+     */
+    @SuppressWarnings("unchecked")
+    public void updateTargetListener(final IProject project) {
+        final List<ITargetChangeListener> listeners =
+            (List<ITargetChangeListener>)mTargetChangeListeners.clone();
+
+        AdtPlugin.getDisplay().asyncExec(new Runnable() {
+            public void run() {
+                for (ITargetChangeListener listener : listeners) {
+                    try {
+                        listener.onProjectTargetChange(project);
+                    } catch (Exception e) {
+                        AdtPlugin.log(e, "Failed to update a TargetChangeListener.");  //$NON-NLS-1$
+                    }
+                }
+            }
+        });
     }
     
     public static synchronized OutputStream getErrorStream() {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
index 96068c2..e71ae47 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/ApkBuilder.java
@@ -203,6 +203,9 @@
         // get a project object
         IProject project = getProject();
 
+        // Top level check to make sure the build can move forward.
+        abortOnBadSetup(project);
+
         // get the list of referenced projects.
         IProject[] referencedProjects = ProjectHelper.getReferencedProjects(project);
         IJavaProject[] referencedJavaProjects = getJavaProjects(referencedProjects);
@@ -262,69 +265,12 @@
                 }
             }
         }
-
-        // do some extra check, in case the output files are not present. This
-        // will force to recreate them.
-        IResource tmp = null;
-
-        if (mPackageResources == false && outputFolder != null) {
-            tmp = outputFolder.findMember(AndroidConstants.FN_RESOURCES_AP_);
-            if (tmp == null || tmp.exists() == false) {
-                mPackageResources = true;
-                mBuildFinalPackage = true;
-            }
-        }
-        if (mConvertToDex == false && outputFolder != null) {
-            tmp = outputFolder.findMember(AndroidConstants.FN_CLASSES_DEX);
-            if (tmp == null || tmp.exists() == false) {
-                mConvertToDex = true;
-                mBuildFinalPackage = true;
-            }
-        }
-
-        // get the extra configs for the project. This will give us a list of custom apk
-        // to build based on a restricted set of resources.
-        Map<String, String> configs = Sdk.getCurrent().getProjectApkConfigs(project);
-
-        // also check the final file(s)!
-        String finalPackageName = getFileName(project, null /*config*/);
-        if (mBuildFinalPackage == false && outputFolder != null) {
-            tmp = outputFolder.findMember(finalPackageName);
-            if (tmp == null || (tmp instanceof IFile &&
-                    tmp.exists() == false)) {
-                String msg = String.format(Messages.s_Missing_Repackaging, finalPackageName);
-                AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
-                mBuildFinalPackage = true;
-            }
-
-            if (configs != null) {
-                Set<Entry<String, String>> entrySet = configs.entrySet();
-                
-                for (Entry<String, String> entry : entrySet) {
-                    String filename = getFileName(project, entry.getKey());
-
-                    tmp = outputFolder.findMember(filename);
-                    if (tmp == null || (tmp instanceof IFile &&
-                            tmp.exists() == false)) {
-                        String msg = String.format(Messages.s_Missing_Repackaging,
-                                finalPackageName);
-                        AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
-                        mBuildFinalPackage = true;
-                        break;
-                    }
-                }
-            }
-        }
-
+        
         // store the build status in the persistent storage
         saveProjectBooleanProperty(PROPERTY_CONVERT_TO_DEX , mConvertToDex);
         saveProjectBooleanProperty(PROPERTY_PACKAGE_RESOURCES, mPackageResources);
         saveProjectBooleanProperty(PROPERTY_BUILD_APK, mBuildFinalPackage);
 
-        // At this point, we can abort the build if we have to, as we have computed
-        // our resource delta and stored the result.
-        abortOnBadSetup(project);
-        
         if (dv != null && dv.mXmlError) {
             AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
             Messages.Xml_Error);
@@ -351,6 +297,82 @@
             return referencedProjects;
         }
 
+        // get the extra configs for the project.
+        // The map contains (name, filter) where 'name' is a name to be used in the apk filename,
+        // and filter is the resource filter to be used in the aapt -c parameters to restrict
+        // which resource configurations to package in the apk.
+        Map<String, String> configs = Sdk.getCurrent().getProjectApkConfigs(project);
+
+        // do some extra check, in case the output files are not present. This
+        // will force to recreate them.
+        IResource tmp = null;
+
+        if (mPackageResources == false) {
+            // check the full resource package
+            tmp = outputFolder.findMember(AndroidConstants.FN_RESOURCES_AP_);
+            if (tmp == null || tmp.exists() == false) {
+                mPackageResources = true;
+                mBuildFinalPackage = true;
+            } else {
+                // if the full package is present, we check the filtered resource packages as well
+                if (configs != null) {
+                    Set<Entry<String, String>> entrySet = configs.entrySet();
+                    
+                    for (Entry<String, String> entry : entrySet) {
+                        String filename = String.format(AndroidConstants.FN_RESOURCES_S_AP_,
+                                entry.getKey());
+    
+                        tmp = outputFolder.findMember(filename);
+                        if (tmp == null || (tmp instanceof IFile &&
+                                tmp.exists() == false)) {
+                            String msg = String.format(Messages.s_Missing_Repackaging, filename);
+                            AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
+                            mPackageResources = true;
+                            mBuildFinalPackage = true;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        // check classes.dex is present. If not we force to recreate it.
+        if (mConvertToDex == false) {
+            tmp = outputFolder.findMember(AndroidConstants.FN_CLASSES_DEX);
+            if (tmp == null || tmp.exists() == false) {
+                mConvertToDex = true;
+                mBuildFinalPackage = true;
+            }
+        }
+
+        // also check the final file(s)!
+        String finalPackageName = getFileName(project, null /*config*/);
+        if (mBuildFinalPackage == false) {
+            tmp = outputFolder.findMember(finalPackageName);
+            if (tmp == null || (tmp instanceof IFile &&
+                    tmp.exists() == false)) {
+                String msg = String.format(Messages.s_Missing_Repackaging, finalPackageName);
+                AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
+                mBuildFinalPackage = true;
+            } else if (configs != null) {
+                // if the full apk is present, we check the filtered apk as well
+                Set<Entry<String, String>> entrySet = configs.entrySet();
+                
+                for (Entry<String, String> entry : entrySet) {
+                    String filename = getFileName(project, entry.getKey());
+
+                    tmp = outputFolder.findMember(filename);
+                    if (tmp == null || (tmp instanceof IFile &&
+                            tmp.exists() == false)) {
+                        String msg = String.format(Messages.s_Missing_Repackaging, filename);
+                        AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
+                        mBuildFinalPackage = true;
+                        break;
+                    }
+                }
+            }
+        }
+
         // at this point we know if we need to recreate the temporary apk
         // or the dex file, but we don't know if we simply need to recreate them
         // because they are missing
@@ -396,6 +418,9 @@
 
             // first we check if we need to package the resources.
             if (mPackageResources) {
+                // remove some aapt_package only markers.
+                removeMarkersFromContainer(project, AndroidConstants.MARKER_AAPT_PACKAGE);
+
                 // need to figure out some path before we can execute aapt;
 
                 // resource to the AndroidManifest.xml file
@@ -552,7 +577,8 @@
      * @param osAssetsPath The path to the assets folder. This can be null.
      * @param osOutFilePath The path to the temporary resource file to create.
      * @param configFilter The configuration filter for the resources to include
-     * (used with -c option)
+     * (used with -c option, for example "port,en,fr" to include portrait, English and French
+     * resources.)
      * @return true if success, false otherwise.
      */
     private boolean executeAapt(IProject project, String osManifestPath,
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java
index 6b0810a..e2e9728 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/BaseBuilder.java
@@ -149,6 +149,15 @@
     private final static Pattern sPattern8Line1 = Pattern.compile(
             "^(invalid resource directory name): (.*)$"); //$NON-NLS-1$
 
+    /**
+     * 2 line aapt error<br>
+     * "ERROR: Invalid configuration: foo"<br>
+     * "                              ^^^"<br>
+     * There's no need to parse the 2nd line.
+     */
+    private final static Pattern sPattern9Line1 = Pattern.compile(
+            "^Invalid configuration: (.+)$"); //$NON-NLS-1$
+
     /** SAX Parser factory. */
     private SAXParserFactory mParserFactory;
 
@@ -440,8 +449,8 @@
                 String location = m.group(1);
 
                 // check the values and attempt to mark the file.
-                if (checkAndMark(location, lineStr, msg, osRoot,
-                        project, IMarker.SEVERITY_ERROR) == false) {
+                if (checkAndMark(location, lineStr, msg, osRoot, project,
+                        AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
                     return true;
                 }
                 continue;
@@ -465,7 +474,7 @@
 
                 // display the error
                 if (checkAndMark(location, null, msg, osRoot, project,
-                        IMarker.SEVERITY_ERROR) == false) {
+                        AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
                     return true;
                 }
 
@@ -488,8 +497,8 @@
                 String lineStr = m.group(2);
 
                 // check the values and attempt to mark the file.
-                if (checkAndMark(location, lineStr, msg, osRoot,
-                        project, IMarker.SEVERITY_ERROR) == false) {
+                if (checkAndMark(location, lineStr, msg, osRoot, project,
+                        AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
                     return true;
                 }
                 continue;
@@ -502,8 +511,8 @@
                 String msg = m.group(3);
 
                 // check the values and attempt to mark the file.
-                if (checkAndMark(location, lineStr, msg, osRoot,
-                        project, IMarker.SEVERITY_ERROR) == false) {
+                if (checkAndMark(location, lineStr, msg, osRoot, project,
+                        AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
                     return true;
                 }
 
@@ -526,8 +535,8 @@
                 String lineStr = m.group(2);
 
                 // check the values and attempt to mark the file.
-                if (checkAndMark(location, lineStr, msg, osRoot,
-                        project, IMarker.SEVERITY_ERROR) == false) {
+                if (checkAndMark(location, lineStr, msg, osRoot, project,
+                        AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
                     return true;
                 }
 
@@ -542,8 +551,8 @@
                 String msg = m.group(3);
 
                 // check the values and attempt to mark the file.
-                if (checkAndMark(location, lineStr, msg, osRoot,
-                        project,IMarker.SEVERITY_WARNING) == false) {
+                if (checkAndMark(location, lineStr, msg, osRoot, project,
+                        AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_WARNING) == false) {
                     return true;
                 }
 
@@ -558,8 +567,8 @@
                 String msg = m.group(3);
 
                 // check the values and attempt to mark the file.
-                if (checkAndMark(location, lineStr, msg, osRoot,
-                        project, IMarker.SEVERITY_ERROR) == false) {
+                if (checkAndMark(location, lineStr, msg, osRoot, project,
+                        AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
                     return true;
                 }
 
@@ -574,7 +583,25 @@
 
                 // check the values and attempt to mark the file.
                 if (checkAndMark(location, null, msg, osRoot, project,
-                        IMarker.SEVERITY_ERROR) == false) {
+                        AndroidConstants.MARKER_AAPT_COMPILE, IMarker.SEVERITY_ERROR) == false) {
+                    return true;
+                }
+
+                // success, go to the next line
+                continue;
+            }
+
+            m = sPattern9Line1.matcher(p);
+            if (m.matches()) {
+                String badConfig = m.group(1);
+                String msg = String.format("APK Configuration filter '%1$s' is invalid", badConfig);
+                
+                // skip the next line
+                i++;
+
+                // check the values and attempt to mark the file.
+                if (checkAndMark(null /*location*/, null, msg, osRoot, project,
+                        AndroidConstants.MARKER_AAPT_PACKAGE, IMarker.SEVERITY_ERROR) == false) {
                     return true;
                 }
 
@@ -659,23 +686,25 @@
     /**
      * Check if the parameters gotten from the error output are valid, and mark
      * the file with an AAPT marker.
-     * @param location
+     * @param location the full OS path of the error file. If null, the project is marked
      * @param lineStr
      * @param message
      * @param root The root directory of the project, in OS specific format.
      * @param project
+     * @param markerId The marker id to put.
      * @param severity The severity of the marker to put (IMarker.SEVERITY_*)
-     * @return true if the parameters were valid and the file was marked
-     * sucessfully.
+     * @return true if the parameters were valid and the file was marked successfully.
      *
      * @see IMarker
      */
     private final  boolean checkAndMark(String location, String lineStr,
-            String message, String root, IProject project, int severity) {
+            String message, String root, IProject project, String markerId, int severity) {
         // check this is in fact a file
-        File f = new File(location);
-        if (f.exists() == false) {
-            return false;
+        if (location != null) {
+            File f = new File(location);
+            if (f.exists() == false) {
+                return false;
+            }
         }
 
         // get the line number
@@ -692,16 +721,18 @@
         }
 
         // add the marker
-        IResource f2 = getResourceFromFullPath(location, root, project);
-        if (f2 == null) {
-            return false;
+        IResource f2 = project;
+        if (location != null) {
+            f2 = getResourceFromFullPath(location, root, project);
+            if (f2 == null) {
+                return false;
+            }
         }
 
         // check if there's a similar marker already, since aapt is launched twice
         boolean markerAlreadyExists = false;
         try {
-            IMarker[] markers = f2.findMarkers(AndroidConstants.MARKER_AAPT, true,
-                    IResource.DEPTH_ZERO);
+            IMarker[] markers = f2.findMarkers(markerId, true, IResource.DEPTH_ZERO);
 
             for (IMarker marker : markers) {
                 int tmpLine = marker.getAttribute(IMarker.LINE_NUMBER, -1);
@@ -732,10 +763,10 @@
 
         if (markerAlreadyExists == false) {
             if (line != -1) {
-                BaseProjectHelper.addMarker(f2, AndroidConstants.MARKER_AAPT, message, line,
+                BaseProjectHelper.addMarker(f2, markerId, message, line,
                         severity);
             } else {
-                BaseProjectHelper.addMarker(f2, AndroidConstants.MARKER_AAPT, message, severity);
+                BaseProjectHelper.addMarker(f2, markerId, message, severity);
             }
         }
 
@@ -851,7 +882,7 @@
      * Aborts the build if the SDK/project setups are broken. This does not
      * display any errors.
      * 
-     * @param javaProject The {@link IJavaProject} being compiled.
+     * @param project The {@link IJavaProject} being compiled.
      * @throws CoreException
      */
     protected final void abortOnBadSetup(IProject project) throws CoreException {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/DexWrapper.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/DexWrapper.java
index 26d96d7..65ad4f5 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/DexWrapper.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/DexWrapper.java
@@ -57,8 +57,11 @@
     private Field mConsoleErr;
     
     /**
-     * Loads the dex library from a file path. The loaded library can be used with the
-     * {@link DexWrapper} object returned by {@link #getWrapper()}
+     * Loads the dex library from a file path.
+     * 
+     * The loaded library can be used via
+     * {@link DexWrapper#run(String, String[], boolean, PrintStream, PrintStream)}.
+     * 
      * @param osFilepath the location of the dex.jar file.
      * @return an IStatus indicating the result of the load.
      */
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
index 958cac2..a0e446c 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/build/PreCompilerBuilder.java
@@ -234,6 +234,10 @@
 
         // get the project objects
         IProject project = getProject();
+        
+        // Top level check to make sure the build can move forward.
+        abortOnBadSetup(project);
+        
         IJavaProject javaProject = JavaCore.create(project);
         IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project);
 
@@ -284,10 +288,6 @@
         saveProjectBooleanProperty(PROPERTY_COMPILE_RESOURCES , mCompileResources);
         // TODO also needs to store the list of aidl to compile/remove
 
-        // At this point we have stored what needs to be build, so we can
-        // do some high level test and abort if needed.
-        abortOnBadSetup(project);
-        
         // if there was some XML errors, we just return w/o doing
         // anything since we've put some markers in the files anyway.
         if (dv != null && dv.mXmlError) {
@@ -381,7 +381,7 @@
                 // mark the manifest file
                 String message = String.format(Messages.Package_s_Doesnt_Exist_Error,
                         mManifestPackage);
-                BaseProjectHelper.addMarker(manifest, AndroidConstants.MARKER_AAPT, message,
+                BaseProjectHelper.addMarker(manifest, AndroidConstants.MARKER_AAPT_COMPILE, message,
                         IMarker.SEVERITY_ERROR);
                 AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, message);
 
@@ -409,8 +409,8 @@
                 String osManifestPath = manifestLocation.toOSString();
 
                 // remove the aapt markers
-                removeMarkersFromFile(manifest, AndroidConstants.MARKER_AAPT);
-                removeMarkersFromContainer(resFolder, AndroidConstants.MARKER_AAPT);
+                removeMarkersFromFile(manifest, AndroidConstants.MARKER_AAPT_COMPILE);
+                removeMarkersFromContainer(resFolder, AndroidConstants.MARKER_AAPT_COMPILE);
 
                 AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
                         Messages.Preparing_Generated_Files);
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/preferences/AndroidPreferencePage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/preferences/AndroidPreferencePage.java
index c27c106..458f78e 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/preferences/AndroidPreferencePage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/preferences/AndroidPreferencePage.java
@@ -17,11 +17,19 @@
 package com.android.ide.eclipse.adt.preferences;
 
 import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.sdk.Sdk;
+import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
+import com.android.sdklib.IAndroidTarget;
+import com.android.sdkuilib.SdkTargetSelector;
 
+import org.eclipse.core.resources.IProject;
 import org.eclipse.jface.preference.DirectoryFieldEditor;
 import org.eclipse.jface.preference.FieldEditorPreferencePage;
 import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Text;
 import org.eclipse.ui.IWorkbench;
 import org.eclipse.ui.IWorkbenchPreferencePage;
@@ -80,6 +88,9 @@
      */
     private static class SdkDirectoryFieldEditor extends DirectoryFieldEditor {
 
+        private SdkTargetSelector mTargetSelector;
+        private TargetChangedListener mTargetChangeListener;
+
         public SdkDirectoryFieldEditor(String name, String labelText, Composite parent) {
             super(name, labelText, parent);
             setEmptyStringAllowed(false);
@@ -131,5 +142,68 @@
             setValidateStrategy(VALIDATE_ON_KEY_STROKE);
             return super.getTextControl(parent);
         }
+
+        /* (non-Javadoc)
+         * Method declared on StringFieldEditor (and FieldEditor).
+         */
+        @Override
+        protected void doFillIntoGrid(Composite parent, int numColumns) {
+            super.doFillIntoGrid(parent, numColumns);
+
+            GridData gd;
+            Label l = new Label(parent, SWT.NONE);
+            l.setText("Note: The list of SDK Targets below is only reloaded once you hit 'Apply' or 'OK'.");
+            gd = new GridData(GridData.FILL_HORIZONTAL);
+            gd.horizontalSpan = numColumns;
+            l.setLayoutData(gd);
+            
+            try {
+                // We may not have an sdk if the sdk path pref is empty or not valid.
+                Sdk sdk = Sdk.getCurrent();
+                IAndroidTarget[] targets = sdk != null ? sdk.getTargets() : null;
+                
+                mTargetSelector = new SdkTargetSelector(parent,
+                        targets,
+                        false, /*allowSelection*/
+                        false /*multipleSelection*/);
+                gd = (GridData) mTargetSelector.getLayoutData();
+                gd.horizontalSpan = numColumns;
+                
+                if (mTargetChangeListener == null) {
+                    mTargetChangeListener = new TargetChangedListener();
+                    AdtPlugin.getDefault().addTargetListener(mTargetChangeListener);
+                }
+            } catch (Exception e) {
+                // We need to catch *any* exception that arises here, otherwise it disables
+                // the whole pref panel. We can live without the Sdk target selector but
+                // not being able to actually set an sdk path.
+                AdtPlugin.log(e, "SdkTargetSelector failed");
+            }
+        }
+        
+        @Override
+        public void dispose() {
+            super.dispose();
+            if (mTargetChangeListener != null) {
+                AdtPlugin.getDefault().removeTargetListener(mTargetChangeListener);
+                mTargetChangeListener = null;
+            }
+        }
+        
+        private class TargetChangedListener implements ITargetChangeListener {
+            public void onProjectTargetChange(IProject changedProject) {
+                // do nothing.
+            }
+
+            public void onTargetsLoaded() {
+                if (mTargetSelector != null) {
+                    // We may not have an sdk if the sdk path pref is empty or not valid.
+                    Sdk sdk = Sdk.getCurrent();
+                    IAndroidTarget[] targets = sdk != null ? sdk.getTargets() : null;
+
+                    mTargetSelector.setTargets(targets);
+                }
+            }
+        }
     }
 }
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java
index 339dcd0..d686830 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/internal/AndroidClasspathContainerInitializer.java
@@ -266,7 +266,6 @@
                     // We schedule a new job to put the marker after.
                     final String fmessage = markerMessage;
                     Job markerJob = new Job("Android SDK: Resolving error markers") {
-                        @SuppressWarnings("unchecked")
                         @Override
                         protected IStatus run(IProgressMonitor monitor) {
                             try {
@@ -296,7 +295,6 @@
                     // In some cases, the workspace may be locked for modification when we pass
                     // here, so we schedule a new job to put the marker after.
                     Job markerJob = new Job("Android SDK: Resolving error markers") {
-                        @SuppressWarnings("unchecked")
                         @Override
                         protected IStatus run(IProgressMonitor monitor) {
                             try {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/properties/AndroidPropertyPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/properties/AndroidPropertyPage.java
index 584dd0d..a4c019f 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/properties/AndroidPropertyPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/project/properties/AndroidPropertyPage.java
@@ -18,6 +18,7 @@
 
 import com.android.ide.eclipse.adt.sdk.Sdk;
 import com.android.sdklib.IAndroidTarget;
+import com.android.sdkuilib.ApkConfigWidget;
 import com.android.sdkuilib.SdkTargetSelector;
 
 import org.eclipse.core.resources.IProject;
@@ -32,6 +33,8 @@
 import org.eclipse.ui.IWorkbenchPropertyPage;
 import org.eclipse.ui.dialogs.PropertyPage;
 
+import java.util.Map;
+
 /**
  * Property page for "Android" project.
  * This is accessible from the Package Explorer when right clicking a project and choosing
@@ -42,6 +45,7 @@
 
     private IProject mProject;
     private SdkTargetSelector mSelector;
+    private ApkConfigWidget mApkConfigWidget;
 
     public AndroidPropertyPage() {
         // pass
@@ -51,7 +55,14 @@
     protected Control createContents(Composite parent) {
         // get the element (this is not yet valid in the constructor).
         mProject = (IProject)getElement();
-        
+
+        // get the targets from the sdk
+        IAndroidTarget[] targets = null;
+        if (Sdk.getCurrent() != null) {
+            targets = Sdk.getCurrent().getTargets();
+        }
+
+        // build the UI.
         Composite top = new Composite(parent, SWT.NONE);
         top.setLayoutData(new GridData(GridData.FILL_BOTH));
         top.setLayout(new GridLayout(1, false));
@@ -59,20 +70,28 @@
         Label l = new Label(top, SWT.NONE);
         l.setText("Project Target");
         
-        // get the targets from the sdk
-        IAndroidTarget[] targets = null;
-        if (Sdk.getCurrent() != null) {
-            targets = Sdk.getCurrent().getTargets();
-        }
-        
-        // build the UI.
         mSelector = new SdkTargetSelector(top, targets, false /*allowMultipleSelection*/);
 
-        if (Sdk.getCurrent() != null) {
-            IAndroidTarget target = Sdk.getCurrent().getTarget(mProject);
+        l = new Label(top, SWT.SEPARATOR | SWT.HORIZONTAL);
+        l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+        l = new Label(top, SWT.NONE);
+        l.setText("Project APK Configurations");
+
+        mApkConfigWidget = new ApkConfigWidget(top);
+
+        // fill the ui
+        Sdk currentSdk = Sdk.getCurrent();
+        if (currentSdk != null && mProject.isOpen()) {
+            // get the target
+            IAndroidTarget target = currentSdk.getTarget(mProject);
             if (target != null) {
                 mSelector.setSelection(target);
             }
+            
+            // get the apk configurations
+            Map<String, String> configs = currentSdk.getProjectApkConfigs(mProject);
+            mApkConfigWidget.fillTable(configs);
         }
 
         mSelector.setSelectionListener(new SelectionAdapter() {
@@ -83,14 +102,20 @@
                 setValid(target != null);
             }
         });
+        
+        if (mProject.isOpen() == false) {
+            // disable the ui.
+        }
 
         return top;
     }
 
     @Override
     public boolean performOk() {
-        if (Sdk.getCurrent() != null) {
-            Sdk.getCurrent().setProject(mProject, mSelector.getFirstSelected());
+        Sdk currentSdk = Sdk.getCurrent();
+        if (currentSdk != null) {
+            currentSdk.setProject(mProject, mSelector.getFirstSelected(),
+                    mApkConfigWidget.getApkConfigs());
         }
         
         return true;
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetData.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetData.java
index 2309181..a8852e7 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetData.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetData.java
@@ -27,6 +27,7 @@
 import com.android.ide.eclipse.editors.xml.descriptors.XmlDescriptors;
 import com.android.layoutlib.api.ILayoutBridge;
 import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
 
 import java.util.Hashtable;
 import java.util.Map;
@@ -43,6 +44,7 @@
     public final static int DESCRIPTOR_RESOURCES = 5;
     public final static int DESCRIPTOR_SEARCHABLE = 6;
     public final static int DESCRIPTOR_PREFERENCES = 7;
+    public final static int DESCRIPTOR_GADGET_PROVIDER = 8;
     
     public final static class LayoutBridge {
         /** Link to the layout bridge */
@@ -51,6 +53,8 @@
         public LoadStatus status = LoadStatus.LOADING;
         
         public ClassLoader classLoader;
+        
+        public int apiLevel;
     }
 
     private final IAndroidTarget mTarget;
@@ -93,6 +97,7 @@
     
     /**
      * Creates an AndroidTargetData object.
+     * @param optionalLibraries 
      */
     void setExtraData(IResourceRepository systemResourceRepository,
             AndroidManifestDescriptors manifestDescriptors,
@@ -105,6 +110,7 @@
             String[] broadcastIntentActionValues,
             String[] serviceIntentActionValues,
             String[] intentCategoryValues,
+            IOptionalLibrary[] optionalLibraries,
             ProjectResources resources,
             LayoutBridge layoutBridge) {
         
@@ -120,8 +126,9 @@
         setPermissions(permissionValues);
         setIntentFilterActionsAndCategories(activityIntentActionValues, broadcastIntentActionValues,
                 serviceIntentActionValues, intentCategoryValues);
+        setOptionalLibraries(optionalLibraries);
     }
-    
+
     public DexWrapper getDexWrapper() {
         return mDexWrapper;
     }
@@ -151,6 +158,8 @@
                 return ResourcesDescriptors.getInstance();
             case DESCRIPTOR_PREFERENCES:
                 return mXmlDescriptors.getPreferencesProvider();
+            case DESCRIPTOR_GADGET_PROVIDER:
+                return mXmlDescriptors.getGadgetProvider();
             case DESCRIPTOR_SEARCHABLE:
                 return mXmlDescriptors.getSearchableProvider();
             default :
@@ -283,6 +292,20 @@
         setValues("(service,action,android:name)", serviceIntentActions); //$NON-NLS-1$
         setValues("(category,android:name)", intentCategoryValues); //$NON-NLS-1$
     }
+    
+    private void setOptionalLibraries(IOptionalLibrary[] optionalLibraries) {
+        String[] values;
+        
+        if (optionalLibraries == null) {
+            values = new String[0];
+        } else {
+            values = new String[optionalLibraries.length];
+            for (int i = 0; i < optionalLibraries.length; i++) {
+                values[i] = optionalLibraries[i].getName();
+            }
+        }
+        setValues("(uses-library,android:name)", values);
+    }
 
     /**
      * Sets a (name, values) pair in the hash map.
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java
index aab660d..04baeba 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/AndroidTargetParser.java
@@ -92,7 +92,7 @@
         try {
             SubMonitor progress = SubMonitor.convert(monitor,
                     String.format("Parsing SDK %1$s", mAndroidTarget.getName()),
-                    200);
+                    14);
             
             AndroidTargetData targetData = new AndroidTargetData(mAndroidTarget);
 
@@ -107,15 +107,14 @@
             
             // we have loaded dx.
             targetData.setDexWrapper(dexWrapper);
+            progress.worked(1);
             
             // parse the rest of the data.
-            progress.setWorkRemaining(120);
 
             AndroidJarLoader classLoader =
                 new AndroidJarLoader(mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR));
             
             preload(classLoader, progress.newChild(40, SubMonitor.SUPPRESS_NONE));
-            progress.setWorkRemaining(80);
             
             if (progress.isCanceled()) {
                 return Status.CANCEL_STATUS;
@@ -124,7 +123,7 @@
             // get the resource Ids.
             progress.subTask("Resource IDs");
             IResourceRepository frameworkRepository = collectResourceIds(classLoader);
-            progress.worked(5);
+            progress.worked(1);
 
             if (progress.isCanceled()) {
                 return Status.CANCEL_STATUS;
@@ -133,7 +132,7 @@
             // get the permissions
             progress.subTask("Permissions");
             String[] permissionValues = collectPermissions(classLoader);
-            progress.worked(5);
+            progress.worked(1);
 
             if (progress.isCanceled()) {
                 return Status.CANCEL_STATUS;
@@ -147,7 +146,7 @@
             ArrayList<String> categories = new ArrayList<String>();
             collectIntentFilterActionsAndCategories(activity_actions, broadcast_actions,
                     service_actions, categories);
-            progress.worked(5);
+            progress.worked(1);
 
             if (progress.isCanceled()) {
                 return Status.CANCEL_STATUS;
@@ -158,12 +157,14 @@
             AttrsXmlParser attrsXmlParser = new AttrsXmlParser(
                     mAndroidTarget.getPath(IAndroidTarget.ATTRIBUTES));
             attrsXmlParser.preload();
+            progress.worked(1);
 
             progress.subTask("Manifest definitions");
             AttrsXmlParser attrsManifestXmlParser = new AttrsXmlParser(
                     mAndroidTarget.getPath(IAndroidTarget.MANIFEST_ATTRIBUTES),
                     attrsXmlParser);
             attrsManifestXmlParser.preload();
+            progress.worked(1);
 
             Collection<ViewClassInfo> mainList = new ArrayList<ViewClassInfo>();
             Collection<ViewClassInfo> groupList = new ArrayList<ViewClassInfo>();
@@ -171,7 +172,7 @@
             // collect the layout/widgets classes
             progress.subTask("Widgets and layouts");
             collectLayoutClasses(classLoader, attrsXmlParser, mainList, groupList,
-                    progress.newChild(40));
+                    progress.newChild(1));
             
             if (progress.isCanceled()) {
                 return Status.CANCEL_STATUS;
@@ -185,7 +186,7 @@
             mainList.clear();
             groupList.clear();
             collectPreferenceClasses(classLoader, attrsXmlParser, mainList, groupList,
-                    progress.newChild(5));
+                    progress.newChild(1));
 
             if (progress.isCanceled()) {
                 return Status.CANCEL_STATUS;
@@ -202,6 +203,11 @@
                                                                             attrsManifestXmlParser);
             Map<String, Map<String, Integer>> enumValueMap = attrsXmlParser.getEnumFlagValues();
 
+            Map<String, DeclareStyleableInfo> xmlGadgetMap = null;
+            if (mAndroidTarget.getApiVersionNumber() >= 3) {
+                xmlGadgetMap = collectGadgetDefinitions(attrsXmlParser);
+            }
+
             if (progress.isCanceled()) {
                 return Status.CANCEL_STATUS;
             }
@@ -210,7 +216,7 @@
             // the PlatformData object.
             AndroidManifestDescriptors manifestDescriptors = new AndroidManifestDescriptors(); 
             manifestDescriptors.updateDescriptors(manifestMap);
-            progress.worked(10);
+            progress.worked(1);
 
             if (progress.isCanceled()) {
                 return Status.CANCEL_STATUS;
@@ -218,7 +224,7 @@
 
             LayoutDescriptors layoutDescriptors = new LayoutDescriptors();
             layoutDescriptors.updateDescriptors(layoutViewsInfo, layoutGroupsInfo);
-            progress.worked(10);
+            progress.worked(1);
 
             if (progress.isCanceled()) {
                 return Status.CANCEL_STATUS;
@@ -226,25 +232,28 @@
 
             MenuDescriptors menuDescriptors = new MenuDescriptors();
             menuDescriptors.updateDescriptors(xmlMenuMap);
-            progress.worked(10);
+            progress.worked(1);
 
             if (progress.isCanceled()) {
                 return Status.CANCEL_STATUS;
             }
 
             XmlDescriptors xmlDescriptors = new XmlDescriptors();
-            xmlDescriptors.updateDescriptors(xmlSearchableMap, preferencesInfo,
+            xmlDescriptors.updateDescriptors(
+                    xmlSearchableMap,
+                    xmlGadgetMap,
+                    preferencesInfo,
                     preferenceGroupsInfo);
-            progress.worked(10);
+            progress.worked(1);
             
             // load the framework resources.
             ProjectResources resources = ResourceManager.getInstance().loadFrameworkResources(
                     mAndroidTarget);
-            progress.worked(10);
+            progress.worked(1);
             
             // now load the layout lib bridge
             LayoutBridge layoutBridge = loadLayoutBridge();
-            progress.worked(10);
+            progress.worked(1);
             
             // and finally create the PlatformData with all that we loaded.
             targetData.setExtraData(frameworkRepository,
@@ -258,6 +267,7 @@
                     broadcast_actions.toArray(new String[broadcast_actions.size()]),
                     service_actions.toArray(new String[service_actions.size()]),
                     categories.toArray(new String[categories.size()]),
+                    mAndroidTarget.getOptionalLibraries(),
                     resources,
                     layoutBridge);
             
@@ -268,10 +278,6 @@
             AdtPlugin.logAndPrintError(e, TAG, "SDK parser failed"); //$NON-NLS-1$
             AdtPlugin.printToConsole("SDK parser failed", e.getMessage());
             return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "SDK parser failed", e);
-        } finally {
-            if (monitor != null) {
-                monitor.done();
-            }
         }
     }
 
@@ -605,6 +611,31 @@
     }
 
     /**
+     * Collects all gadgetProviderInfo definition information from the attrs.xml and returns it.
+     * 
+     * @param attrsXmlParser The parser of the attrs.xml file
+     */
+    private Map<String, DeclareStyleableInfo> collectGadgetDefinitions(
+            AttrsXmlParser attrsXmlParser) {
+        Map<String, DeclareStyleableInfo> map = attrsXmlParser.getDeclareStyleableList();
+        Map<String, DeclareStyleableInfo> map2 = new HashMap<String, DeclareStyleableInfo>();
+        for (String key : new String[] { "GadgetProviderInfo" }) {  //$NON-NLS-1$
+            if (map.containsKey(key)) {
+                map2.put(key, map.get(key));
+            } else {
+                AdtPlugin.log(IStatus.WARNING,
+                        "Gadget declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$
+                        key, attrsXmlParser.getOsAttrsXmlPath());
+                AdtPlugin.printErrorToConsole("Android Framework Parser",
+                        String.format("Gadget declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$
+                        key, attrsXmlParser.getOsAttrsXmlPath()));
+            }
+        }
+
+        return Collections.unmodifiableMap(map2);
+    }
+
+    /**
      * Collects all manifest definition information from the attrs_manifest.xml and returns it.
      */
     private Map<String, DeclareStyleableInfo> collectManifestDefinitions(
@@ -650,6 +681,15 @@
                     layoutBridge.status = LoadStatus.FAILED;
                     AdtPlugin.log(IStatus.ERROR, "Failed to load " + AndroidConstants.CLASS_BRIDGE); //$NON-NLS-1$
                 } else {
+                    // get the api level
+                    try {
+                        layoutBridge.apiLevel = layoutBridge.bridge.getApiLevel();
+                    } catch (AbstractMethodError e) {
+                        // the first version of the api did not have this method
+                        layoutBridge.apiLevel = 1;
+                    }
+                    
+                    // and mark the lib as loaded.
                     layoutBridge.status = LoadStatus.LOADED;
                 }
             }
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java
index c7773cc..ba0b568 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/sdk/Sdk.java
@@ -18,6 +18,7 @@
 
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.project.internal.AndroidClasspathContainerInitializer;
+import com.android.ide.eclipse.adt.sdk.AndroidTargetData.LayoutBridge;
 import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor;
 import com.android.ide.eclipse.editors.resources.manager.ResourceMonitor.IProjectListener;
 import com.android.prefs.AndroidLocation.AndroidLocationException;
@@ -33,6 +34,7 @@
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IncrementalProjectBuilder;
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.JavaCore;
@@ -69,6 +71,22 @@
     private final String mDocBaseUrl;
     
     /**
+     * Classes implementing this interface will receive notification when targets are changed.
+     */
+    public interface ITargetChangeListener {
+        /**
+         * Sent when project has its target changed.
+         */
+        void onProjectTargetChange(IProject changedProject);
+        
+        /**
+         * Called when the targets are loaded (either the SDK finished loading when Eclipse starts,
+         * or the SDK is changed).
+         */
+        void onTargetsLoaded();
+    }
+    
+    /**
      * Loads an SDK and returns an {@link Sdk} object if success.
      * @param sdkLocation the OS path to the SDK.
      */
@@ -163,24 +181,87 @@
     }
     
     /**
-     * Associates an {@link IProject} and an {@link IAndroidTarget}.
+     * Sets a new target and a new list of Apk configuration for a given project.
+     * 
+     * @param project the project to receive the new apk configurations
+     * @param target The new target to set, or <code>null</code> to not change the current target.
+     * @param apkConfigMap a map of apk configurations. The map contains (name, filter) where name
+     * is the name of the configuration (a-zA-Z0-9 only), and filter is the comma separated list of
+     * resource configuration to include in the apk (see aapt -c). Can be <code>null</code> if the
+     * apk configurations should not be updated.
      */
-    public void setProject(IProject project, IAndroidTarget target) {
+    public void setProject(IProject project, IAndroidTarget target,
+            Map<String, String> apkConfigMap) {
         synchronized (mProjectTargetMap) {
-            // look for the current target of the project
-            IAndroidTarget previousTarget = mProjectTargetMap.get(project);
-            
-            if (target != previousTarget) {
-                // save the target hash string in the project persistent property
-                setProjectTargetHashString(project, target.hashString());
-                
-                // put it in a local map for easy access.
-                mProjectTargetMap.put(project, target);
+            boolean resolveProject = false;
+            boolean compileProject = false;
+            boolean cleanProject = false;
 
-                // recompile the project if needed.
+            ProjectProperties properties = ProjectProperties.load(
+                    project.getLocation().toOSString(), PropertyType.DEFAULT);
+            if (properties == null) {
+                // doesn't exist yet? we create it.
+                properties = ProjectProperties.create(project.getLocation().toOSString(),
+                        PropertyType.DEFAULT);
+            }
+
+            if (target != null) {
+                // look for the current target of the project
+                IAndroidTarget previousTarget = mProjectTargetMap.get(project);
+
+                if (target != previousTarget) {
+                    // save the target hash string in the project persistent property
+                    properties.setAndroidTarget(target);
+                    
+                    // put it in a local map for easy access.
+                    mProjectTargetMap.put(project, target);
+                    
+                    resolveProject = true;
+                }
+            }
+            
+            if (apkConfigMap != null) {
+                // save the apk configs in the project persistent property
+                cleanProject = ApkConfigurationHelper.setConfigs(properties, apkConfigMap);
+
+                // put it in a local map for easy access.
+                mProjectApkConfigMap.put(project, apkConfigMap);
+                
+                compileProject = true;
+            }
+
+            // we are done with the modification. Save the property file.
+            try {
+                properties.save();
+            } catch (IOException e) {
+                AdtPlugin.log(e, "Failed to save default.properties for project '%s'",
+                        project.getName());
+            }
+            
+            if (resolveProject) {
+                // force a resolve of the project by updating the classpath container.
                 IJavaProject javaProject = JavaCore.create(project);
                 AndroidClasspathContainerInitializer.updateProjects(
                         new IJavaProject[] { javaProject });
+            } else if (compileProject) {
+                // If there was removed configs, we clean instead of build
+                // (to remove the obsolete ap_ and apk file from removed configs).
+                try {
+                    project.build(cleanProject ?
+                                IncrementalProjectBuilder.CLEAN_BUILD :
+                                IncrementalProjectBuilder.FULL_BUILD,
+                            null);
+                } catch (CoreException e) {
+                    // failed to build? force resolve instead.
+                    IJavaProject javaProject = JavaCore.create(project);
+                    AndroidClasspathContainerInitializer.updateProjects(
+                            new IJavaProject[] { javaProject });
+                }
+            }
+            
+            // finally, update the opened editors.
+            if (resolveProject) {
+                AdtPlugin.getDefault().updateTargetListener(project);
             }
         }
     }
@@ -218,7 +299,12 @@
      */
     private static String loadProjectProperties(IProject project, Sdk sdkStorage) {
         // load the default.properties from the project folder.
-        ProjectProperties properties = ProjectProperties.load(project.getLocation().toOSString(),
+        IPath location = project.getLocation();
+        if (location == null) {  // can return null when the project is being deleted.
+            // do nothing and return null;
+            return null;
+        }
+        ProjectProperties properties = ProjectProperties.load(location.toOSString(),
                 PropertyType.DEFAULT);
         if (properties == null) {
             AdtPlugin.log(IStatus.ERROR, "Failed to load properties file for project '%s'",
@@ -229,7 +315,7 @@
         if (sdkStorage != null) {
             Map<String, String> configMap = ApkConfigurationHelper.getConfigs(properties);
             
-            if (configMap.size() > 0) {
+            if (configMap != null) {
                 sdkStorage.mProjectApkConfigMap.put(project, configMap);
             }
         }
@@ -296,40 +382,6 @@
         return mProjectApkConfigMap.get(project);
     }
     
-    public void setProjectApkConfigs(IProject project, Map<String, String> configMap)
-            throws CoreException {
-        // first set the new map
-        mProjectApkConfigMap.put(project, configMap);
-        
-        // Now we write this in default.properties.
-        // Because we don't want to erase other properties from default.properties, we first load
-        // them
-        ProjectProperties properties = ProjectProperties.load(project.getLocation().toOSString(),
-                PropertyType.DEFAULT);
-        if (properties == null) {
-            // doesn't exist yet? we create it.
-            properties = ProjectProperties.create(project.getLocation().toOSString(),
-                    PropertyType.DEFAULT);
-        }
-        
-        // sets the configs in the property file.
-        boolean hasRemovedConfig = ApkConfigurationHelper.setConfigs(properties, configMap);
-
-        // and rewrite the file.
-        try {
-            properties.save();
-        } catch (IOException e) {
-            AdtPlugin.log(e, "Failed to save default.properties for project '%s'",
-                    project.getName());
-        }
-
-        // we're done, force a rebuild. If there was removed config, we clean instead of build
-        // (to remove the obsolete ap_ and apk file from removed configs). 
-        project.build(hasRemovedConfig ?
-                IncrementalProjectBuilder.CLEAN_BUILD : IncrementalProjectBuilder.FULL_BUILD,
-                null);
-    }
-
     /**
      * Returns the {@link AvdManager}. If the AvdManager failed to parse the AVD folder, this could
      * be <code>null</code>.
@@ -402,8 +454,24 @@
     }
 
     public void projectClosed(IProject project) {
-        mProjectTargetMap.remove(project);
-        mProjectApkConfigMap.remove(project);
+        // get the target project
+        synchronized (mProjectTargetMap) {
+            IAndroidTarget target = mProjectTargetMap.get(project);
+            if (target != null) {
+                // get the bridge for the target, and clear the cache for this project.
+                AndroidTargetData data = mTargetDataMap.get(target);
+                if (data != null) {
+                    LayoutBridge bridge = data.getLayoutBridge();
+                    if (bridge != null && bridge.status == LoadStatus.LOADED) {
+                        bridge.bridge.clearCaches(project);
+                    }
+                }
+            }
+            
+            // now remove the project for the maps.
+            mProjectTargetMap.remove(project);
+            mProjectApkConfigMap.remove(project);
+        }
     }
 
     public void projectDeleted(IProject project) {
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewProjectAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewProjectAction.java
new file mode 100644
index 0000000..e0d0d5e
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewProjectAction.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.ide.eclipse.adt.wizards.actions;
+
+import com.android.ide.eclipse.adt.wizards.newproject.NewProjectWizard;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.ui.IWorkbenchWizard;
+
+/**
+ * Delegate for the toolbar action "Android Project".
+ * It displays the Android New Project wizard.
+ */
+public class NewProjectAction extends OpenWizardAction {
+
+    @Override
+    protected IWorkbenchWizard instanciateWizard(IAction action) {
+        return new NewProjectWizard();
+    }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileAction.java
new file mode 100644
index 0000000..8c4a115
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/NewXmlFileAction.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.ide.eclipse.adt.wizards.actions;
+
+import com.android.ide.eclipse.editors.wizards.NewXmlFileWizard;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.ui.IWorkbenchWizard;
+
+/**
+ * Delegate for the toolbar action "Android Project".
+ * It displays the Android New XML file wizard.
+ */
+public class NewXmlFileAction extends OpenWizardAction {
+
+    @Override
+    protected IWorkbenchWizard instanciateWizard(IAction action) {
+        return new NewXmlFileWizard();
+    }
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/OpenWizardAction.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/OpenWizardAction.java
new file mode 100644
index 0000000..4fc9dee
--- /dev/null
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/actions/OpenWizardAction.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.ide.eclipse.adt.wizards.actions;
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+import org.eclipse.ui.IWorkbenchWizard;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.internal.IWorkbenchHelpContextIds;
+import org.eclipse.ui.internal.LegacyResourceSupport;
+import org.eclipse.ui.internal.actions.NewWizardShortcutAction;
+import org.eclipse.ui.internal.util.Util;
+
+/**
+ * An abstract action that displays one of our wizards.
+ * Derived classes must provide the actual wizard to display.
+ */
+/*package*/ abstract class OpenWizardAction implements IWorkbenchWindowActionDelegate {
+
+    /**
+     * The wizard dialog width, extracted from {@link NewWizardShortcutAction}
+     */
+    private static final int SIZING_WIZARD_WIDTH = 500;
+
+    /**
+     * The wizard dialog height, extracted from {@link NewWizardShortcutAction}
+     */
+    private static final int SIZING_WIZARD_HEIGHT = 500;
+
+    
+    /* (non-Javadoc)
+     * @see org.eclipse.ui.IWorkbenchWindowActionDelegate#dispose()
+     */
+    public void dispose() {
+        // pass
+    }
+
+    /* (non-Javadoc)
+     * @see org.eclipse.ui.IWorkbenchWindowActionDelegate#init(org.eclipse.ui.IWorkbenchWindow)
+     */
+    public void init(IWorkbenchWindow window) {
+        // pass
+    }
+
+    /**
+     * Opens and display the Android New Project Wizard.
+     * <p/>
+     * Most of this implementation is extracted from {@link NewWizardShortcutAction#run()}.
+     * 
+     * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
+     */
+    public void run(IAction action) {
+
+        // get the workbench and the current window
+        IWorkbench workbench = PlatformUI.getWorkbench();
+        IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
+        
+        // This code from NewWizardShortcutAction#run() gets the current window selection
+        // and converts it to a workbench structured selection for the wizard, if possible.
+        ISelection selection = window.getSelectionService().getSelection();
+        IStructuredSelection selectionToPass = StructuredSelection.EMPTY;
+        if (selection instanceof IStructuredSelection) {
+            selectionToPass = (IStructuredSelection) selection;
+        } else {
+            // Build the selection from the IFile of the editor
+            IWorkbenchPart part = window.getPartService().getActivePart();
+            if (part instanceof IEditorPart) {
+                IEditorInput input = ((IEditorPart) part).getEditorInput();
+                Class<?> fileClass = LegacyResourceSupport.getFileClass();
+                if (input != null && fileClass != null) {
+                    Object file = Util.getAdapter(input, fileClass);
+                    if (file != null) {
+                        selectionToPass = new StructuredSelection(file);
+                    }
+                }
+            }
+        }
+
+        // Create the wizard and initialize it with the selection
+        IWorkbenchWizard wizard = instanciateWizard(action);
+        wizard.init(workbench, selectionToPass);
+        
+        // It's not visible yet until a dialog is created and opened
+        Shell parent = window.getShell();
+        WizardDialog dialog = new WizardDialog(parent, wizard);
+        dialog.create();
+        
+        // This code comes straight from NewWizardShortcutAction#run()
+        Point defaultSize = dialog.getShell().getSize();
+        dialog.getShell().setSize(
+                Math.max(SIZING_WIZARD_WIDTH, defaultSize.x),
+                Math.max(SIZING_WIZARD_HEIGHT, defaultSize.y));
+        window.getWorkbench().getHelpSystem().setHelp(dialog.getShell(),
+                IWorkbenchHelpContextIds.NEW_WIZARD_SHORTCUT);
+        
+        dialog.open();
+    }
+
+    /**
+     * Called by {@link #run(IAction)} to instantiate the actual wizard.
+     * 
+     * @param action The action parameter from {@link #run(IAction)}.
+     * @return A new wizard instance. Must not be null.
+     */
+    protected abstract IWorkbenchWizard instanciateWizard(IAction action);
+
+    /* (non-Javadoc)
+     * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction, org.eclipse.jface.viewers.ISelection)
+     */
+    public void selectionChanged(IAction action, ISelection selection) {
+        // pass
+    }
+
+}
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectWizard.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectWizard.java
index 607159a..cb79796 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectWizard.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/wizards/newproject/NewProjectWizard.java
@@ -105,6 +105,8 @@
         SdkConstants.FD_LAYOUT + AndroidConstants.WS_SEP;
     private static final String VALUES_DIRECTORY =
         SdkConstants.FD_VALUES + AndroidConstants.WS_SEP;
+    private static final String GEN_SRC_DIRECTORY =
+        SdkConstants.FD_GEN_SOURCES + AndroidConstants.WS_SEP;
 
     private static final String TEMPLATES_DIRECTORY = "templates/"; //$NON-NLS-1$
     private static final String TEMPLATE_MANIFEST = TEMPLATES_DIRECTORY
@@ -114,7 +116,7 @@
     private static final String TEMPLATE_USES_SDK = TEMPLATES_DIRECTORY
             + "uses-sdk.template"; //$NON-NLS-1$
     private static final String TEMPLATE_INTENT_LAUNCHER = TEMPLATES_DIRECTORY
-    		+ "launcher_intent_filter.template"; //$NON-NLS-1$
+            + "launcher_intent_filter.template"; //$NON-NLS-1$
 
     private static final String TEMPLATE_STRINGS = TEMPLATES_DIRECTORY
             + "strings.template"; //$NON-NLS-1$
@@ -341,15 +343,20 @@
 
             // Create folders in the project if they don't already exist
             addDefaultDirectories(project, AndroidConstants.WS_ROOT, DEFAULT_DIRECTORIES, monitor);
-            String[] sourceFolder = new String[] { (String) parameters.get(PARAM_SRC_FOLDER) };
-            addDefaultDirectories(project, AndroidConstants.WS_ROOT, sourceFolder, monitor);
+            String[] sourceFolders = new String[] {
+                        (String) parameters.get(PARAM_SRC_FOLDER),
+                        GEN_SRC_DIRECTORY
+                    };
+            addDefaultDirectories(project, AndroidConstants.WS_ROOT, sourceFolders, monitor);
 
             // Create the resource folders in the project if they don't already exist.
             addDefaultDirectories(project, RES_DIRECTORY, RES_DIRECTORIES, monitor);
 
             // Setup class path
             IJavaProject javaProject = JavaCore.create(project);
-            setupSourceFolder(javaProject, sourceFolder[0], monitor);
+            for (String sourceFolder : sourceFolders) {
+                setupSourceFolder(javaProject, sourceFolder, monitor);
+            }
 
             if (((Boolean) parameters.get(PARAM_IS_NEW_PROJECT)).booleanValue()) {
                 // Create files in the project if they don't already exist
@@ -359,7 +366,7 @@
                 addIcon(project, monitor);
 
                 // Create the default package components
-                addSampleCode(project, sourceFolder[0], parameters, stringDictionary, monitor);
+                addSampleCode(project, sourceFolders[0], parameters, stringDictionary, monitor);
 
                 // add the string definition file if needed
                 if (stringDictionary.size() > 0) {
@@ -371,7 +378,8 @@
                         monitor);
             }
 
-            Sdk.getCurrent().setProject(project, (IAndroidTarget) parameters.get(PARAM_SDK_TARGET));
+            Sdk.getCurrent().setProject(project, (IAndroidTarget) parameters.get(PARAM_SDK_TARGET),
+                    null /* apkConfigMap*/);
             
             // Fix the project to make sure all properties are as expected.
             // Necessary for existing projects and good for new ones to.
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
index b1c57a6..e201132 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/common/AndroidConstants.java
@@ -148,8 +148,11 @@
     /** The old common plug-in ID. Please do not use for new features. */
     public static final String COMMON_PLUGIN_ID = "com.android.ide.eclipse.common"; //$NON-NLS-1$
 
-    /** aapt marker error. */
-    public final static String MARKER_AAPT = COMMON_PLUGIN_ID + ".aaptProblem"; //$NON-NLS-1$
+    /** aapt marker error when running the compile command */
+    public final static String MARKER_AAPT_COMPILE = COMMON_PLUGIN_ID + ".aaptProblem"; //$NON-NLS-1$
+
+    /** aapt marker error when running the package command */
+    public final static String MARKER_AAPT_PACKAGE = COMMON_PLUGIN_ID + ".aapt2Problem"; //$NON-NLS-1$
 
     /** XML marker error. */
     public final static String MARKER_XML = COMMON_PLUGIN_ID + ".xmlProblem"; //$NON-NLS-1$
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidEditor.java
index dca7db0..c7541e9 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidEditor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/AndroidEditor.java
@@ -19,6 +19,7 @@
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
 import com.android.ide.eclipse.adt.sdk.Sdk;
+import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
 import com.android.ide.eclipse.editors.uimodel.UiElementNode;
 import com.android.sdklib.IAndroidTarget;
 
@@ -97,8 +98,9 @@
     private StructuredTextEditor mTextEditor;
     /** Listener for the XML model from the StructuredEditor */
     private XmlModelStateListener mXmlModelStateListener;
-    /** Listener to update the root node if the resource framework changes */
-    private Runnable mResourceRefreshListener;
+    /** Listener to update the root node if the target of the file is changed because of a
+     * SDK location change or a project target change */
+    private ITargetChangeListener mTargetListener;
 
     /**
      * Creates a form editor.
@@ -107,15 +109,21 @@
         super();
         ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
         
-        mResourceRefreshListener = new Runnable() {
-            public void run() {
-                commitPages(false /* onSave */);
+        mTargetListener = new ITargetChangeListener() {
+            public void onProjectTargetChange(IProject changedProject) {
+                if (changedProject == getProject()) {
+                    onTargetsLoaded();
+                }
+            }
 
+            public void onTargetsLoaded() {
+                commitPages(false /* onSave */);
+                
                 // recreate the ui root node always
                 initUiRootNode(true /*force*/);
             }
         };
-        AdtPlugin.getDefault().addResourceChangedListener(mResourceRefreshListener);
+        AdtPlugin.getDefault().addTargetListener(mTargetListener);
     }
 
     // ---- Abstract Methods ----
@@ -340,9 +348,9 @@
         }
         ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
 
-        if (mResourceRefreshListener != null) {
-            AdtPlugin.getDefault().removeResourceChangedListener(mResourceRefreshListener);
-            mResourceRefreshListener = null;
+        if (mTargetListener != null) {
+            AdtPlugin.getDefault().removeTargetListener(mTargetListener);
+            mTargetListener = null;
         }
 
         super.dispose();
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/DescriptorsUtils.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/DescriptorsUtils.java
index cc923bf..f1d62a1 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/DescriptorsUtils.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/descriptors/DescriptorsUtils.java
@@ -313,7 +313,7 @@
      * 
      * @param attributes The list of {@link AttributeDescriptor} to compare to.
      * @param nsUri The URI of the attribute. Can be null if attribute has no namespace.
-     *              See {@link AndroidConstants#NS_RESOURCES} for a common value.
+     *              See {@link SdkConstants#NS_RESOURCES} for a common value.
      * @param info The {@link AttributeInfo} to know whether it is included in the above list.
      * @return True if this {@link AttributeInfo} is already present in
      *         the {@link AttributeDescriptor} list.
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java
index ca7cac5..eb7dee6 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/GraphicalLayoutEditor.java
@@ -21,6 +21,7 @@
 import com.android.ide.eclipse.adt.sdk.LoadStatus;
 import com.android.ide.eclipse.adt.sdk.Sdk;
 import com.android.ide.eclipse.adt.sdk.AndroidTargetData.LayoutBridge;
+import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
 import com.android.ide.eclipse.common.resources.ResourceType;
 import com.android.ide.eclipse.editors.IconFactory;
 import com.android.ide.eclipse.editors.layout.LayoutEditor.UiEditorActions;
@@ -198,13 +199,21 @@
     private ProjectCallback mProjectCallback;
     private ILayoutLog mLogger;
 
+    private boolean mNeedsXmlReload = false;
     private boolean mNeedsRecompute = false;
     private int mPlatformThemeCount = 0;
     private boolean mDisableUpdates = false;
-    private boolean mActive = false;
 
-    private Runnable mFrameworkResourceChangeListener = new Runnable() {
-        public void run() {
+    /** Listener to update the root node if the target of the file is changed because of a
+     * SDK location change or a project target change */
+    private ITargetChangeListener mTargetListener = new ITargetChangeListener() {
+        public void onProjectTargetChange(IProject changedProject) {
+            if (changedProject == getLayoutEditor().getProject()) {
+                onTargetsLoaded();
+            }
+        }
+
+        public void onTargetsLoaded() {
             // because the SDK changed we must reset the configured framework resource.
             mConfiguredFrameworkRes = null;
             
@@ -228,7 +237,7 @@
 
     private final Runnable mConditionalRecomputeRunnable = new Runnable() {
         public void run() {
-            if (mActive) {
+            if (mLayoutEditor.isGraphicalEditorActive()) {
                 recomputeLayout();
             } else {
                 mNeedsRecompute = true;
@@ -253,7 +262,7 @@
         mMatchImage = factory.getIcon("match"); //$NON-NLS-1$
         mErrorImage = factory.getIcon("error"); //$NON-NLS-1$
 
-        AdtPlugin.getDefault().addResourceChangedListener(mFrameworkResourceChangeListener);
+        AdtPlugin.getDefault().addTargetListener(mTargetListener);
     }
 
     // ------------------------------------
@@ -561,10 +570,9 @@
 
     @Override
     public void dispose() {
-        if (mFrameworkResourceChangeListener != null) {
-            AdtPlugin.getDefault().removeResourceChangedListener(
-                    mFrameworkResourceChangeListener);
-            mFrameworkResourceChangeListener = null;
+        if (mTargetListener != null) {
+            AdtPlugin.getDefault().removeTargetListener(mTargetListener);
+            mTargetListener = null;
         }
 
         LayoutReloadMonitor.getMonitor().removeListener(mEditedFile.getProject(), this);
@@ -1026,25 +1034,36 @@
     }
 
     /**
-     * Update the layout editor when the Xml model is changed.
+     * Callback for XML model changed. Only update/recompute the layout if the editor is visible
      */
     void onXmlModelChanged() {
-        GraphicalViewer viewer = getGraphicalViewer();
-
-        // try to preserve the selection before changing the content
-        SelectionManager selMan = viewer.getSelectionManager();
-        ISelection selection = selMan.getSelection();
-
-        try {
-            viewer.setContents(getModel());
-        } finally {
-            selMan.setSelection(selection);
-        }
-
         if (mLayoutEditor.isGraphicalEditorActive()) {
+            doXmlReload(true /* force */);
             recomputeLayout();
         } else {
-            mNeedsRecompute = true;
+            mNeedsXmlReload = true;
+        }
+    }
+    
+    /**
+     * Actually performs the XML reload
+     * @see #onXmlModelChanged()
+     */
+    private void doXmlReload(boolean force) {
+        if (force || mNeedsXmlReload) {
+            GraphicalViewer viewer = getGraphicalViewer();
+            
+            // try to preserve the selection before changing the content
+            SelectionManager selMan = viewer.getSelectionManager();
+            ISelection selection = selMan.getSelection();
+    
+            try {
+                viewer.setContents(getModel());
+            } finally {
+                selMan.setSelection(selection);
+            }
+            
+            mNeedsXmlReload = false;
         }
     }
 
@@ -1648,7 +1667,9 @@
     /**
      * Recomputes the layout with the help of layoutlib.
      */
+    @SuppressWarnings("deprecation")
     void recomputeLayout() {
+        doXmlReload(false /* force */);
         try {
             // check that the resource exists. If the file is opened but the project is closed
             // or deleted for some reason (changed from outside of eclipse), then this will
@@ -1763,20 +1784,47 @@
                         if (themeIndex != -1) {
                             String theme = mThemeCombo.getItem(themeIndex);
                             
-                            // change the string if it's a custom theme to make sure we can
-                            // differentiate them
-                            if (themeIndex >= mPlatformThemeCount) {
-                                theme = "*" + theme; //$NON-NLS-1$
-                            }
-    
                             // Compute the layout
                             UiElementPullParser parser = new UiElementPullParser(getModel());
                             Rectangle rect = getBounds();
-                            ILayoutResult result = bridge.bridge.computeLayout(parser,
-                                    iProject /* projectKey */,
-                                    rect.width, rect.height, theme,
-                                    mConfiguredProjectRes, frameworkResources, mProjectCallback,
-                                    mLogger);
+                            ILayoutResult result = null;
+                            if (bridge.apiLevel >= 3) {
+                                // call the new api with proper theme differentiator and
+                                // density/dpi support.
+                                boolean isProjectTheme = themeIndex >= mPlatformThemeCount;
+
+                                // FIXME pass the density/dpi from somewhere (resource config or skin).
+                                result = bridge.bridge.computeLayout(parser,
+                                        iProject /* projectKey */,
+                                        rect.width, rect.height, 160, 160.f, 160.f, 
+                                        theme, isProjectTheme,
+                                        mConfiguredProjectRes, frameworkResources, mProjectCallback,
+                                        mLogger);
+                            } else if (bridge.apiLevel == 2) {
+                                // api with boolean for separation of project/framework theme
+                                boolean isProjectTheme = themeIndex >= mPlatformThemeCount;
+
+                                result = bridge.bridge.computeLayout(parser,
+                                        iProject /* projectKey */,
+                                        rect.width, rect.height, theme, isProjectTheme,
+                                        mConfiguredProjectRes, frameworkResources, mProjectCallback,
+                                        mLogger);
+                            } else {
+                                // oldest api with no density/dpi, and project theme boolean mixed
+                                // into the theme name.
+
+                                // change the string if it's a custom theme to make sure we can
+                                // differentiate them
+                                if (themeIndex >= mPlatformThemeCount) {
+                                    theme = "*" + theme; //$NON-NLS-1$
+                                }
+        
+                                result = bridge.bridge.computeLayout(parser,
+                                        iProject /* projectKey */,
+                                        rect.width, rect.height, theme,
+                                        mConfiguredProjectRes, frameworkResources, mProjectCallback,
+                                        mLogger);
+                            }
     
                             // update the UiElementNode with the layout info.
                             if (result.getSuccess() == ILayoutResult.SUCCESS) {
@@ -1921,8 +1969,7 @@
      * Responds to a page change that made the Graphical editor page the activated page.
      */
     void activated() {
-    	mActive = true;
-        if (mNeedsRecompute) {
+        if (mNeedsRecompute || mNeedsXmlReload) {
             recomputeLayout();
         }
     }
@@ -1931,7 +1978,7 @@
      * Responds to a page change that made the Graphical editor page the deactivated page
      */
     void deactivated() {
-    	mActive = false;
+        // nothing to be done here for now.
     }
 
     /**
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java
index 880ee2b..dabe797 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/layout/LayoutEditor.java
@@ -269,8 +269,12 @@
     protected void pageChange(int newPageIndex) {
         super.pageChange(newPageIndex);
         
-        if (mGraphicalEditor != null && newPageIndex == mGraphicalEditorIndex) {
-            mGraphicalEditor.activated();
+        if (mGraphicalEditor != null) {
+            if (newPageIndex == mGraphicalEditorIndex) {
+                mGraphicalEditor.activated();
+            } else {
+                mGraphicalEditor.deactivated();
+            }
         }
     }
     
@@ -278,8 +282,12 @@
     
     public void partActivated(IWorkbenchPart part) {
         if (part == this) {
-            if (mGraphicalEditor != null && getActivePage() == mGraphicalEditorIndex) {
-                mGraphicalEditor.activated();
+            if (mGraphicalEditor != null) {
+                if (getActivePage() == mGraphicalEditorIndex) {
+                    mGraphicalEditor.activated();
+                } else {
+                    mGraphicalEditor.deactivated();
+                }
             }
         }
     }
@@ -334,23 +342,23 @@
     // ---- Local Methods ----
     
     /**
-     * Returns true if the Graphics editor page is visible.
-     * This <b>must</b> be called from the UI thread.
+     * Returns true if the Graphics editor page is visible. This <b>must</b> be
+     * called from the UI thread.
      */
     boolean isGraphicalEditorActive() {
         IWorkbenchPartSite workbenchSite = getSite();
         IWorkbenchPage workbenchPage = workbenchSite.getPage();
-        
+
         // check if the editor is visible in the workbench page
-        if (workbenchPage.isPartVisible(this)) {
+        if (workbenchPage.isPartVisible(this) && workbenchPage.getActiveEditor() == this) {
             // and then if the page of the editor is visible (not to be confused with
             // the workbench page)
             return mGraphicalEditorIndex == getActivePage();
         }
-        
-        return false;
-    }
 
+        return false;
+    }   
+    
     @Override
     protected void initUiRootNode(boolean force) {
         // The root UI node is always created, even if there's no corresponding XML node.
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java
index 61b73a2..77c08b5 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/AndroidManifestDescriptors.java
@@ -195,6 +195,8 @@
         overrides.put("*/permission",    ListAttributeDescriptor.class);    //$NON-NLS-1$
         overrides.put("*/targetPackage", PackageAttributeDescriptor.class); //$NON-NLS-1$
         
+        overrides.put("uses-library/name", ListAttributeDescriptor.class);       //$NON-NLS-1$
+
         overrides.put("action,category,uses-permission/" + ANDROID_NAME_ATTR,    //$NON-NLS-1$
                       ListAttributeDescriptor.class);
         overrides.put("application/" + ANDROID_NAME_ATTR, ApplicationAttributeDescriptor.class);  //$NON-NLS-1$
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/ClassAttributeDescriptor.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/ClassAttributeDescriptor.java
index abaf438..629b37c 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/ClassAttributeDescriptor.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/manifest/descriptors/ClassAttributeDescriptor.java
@@ -16,12 +16,12 @@
 
 package com.android.ide.eclipse.editors.manifest.descriptors;
 
-import com.android.ide.eclipse.common.AndroidConstants;
 import com.android.ide.eclipse.editors.descriptors.TextAttributeDescriptor;
 import com.android.ide.eclipse.editors.manifest.model.UiClassAttributeNode;
 import com.android.ide.eclipse.editors.manifest.model.UiClassAttributeNode.IPostTypeCreationAction;
 import com.android.ide.eclipse.editors.uimodel.UiAttributeNode;
 import com.android.ide.eclipse.editors.uimodel.UiElementNode;
+import com.android.sdklib.SdkConstants;
 
 /**
  * Describes an XML attribute representing a class name.
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiTreeBlock.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiTreeBlock.java
index e3255d9..fc384e8 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiTreeBlock.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/ui/tree/UiTreeBlock.java
@@ -17,6 +17,7 @@
 package com.android.ide.eclipse.editors.ui.tree;
 
 import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.sdk.Sdk.ITargetChangeListener;
 import com.android.ide.eclipse.editors.AndroidEditor;
 import com.android.ide.eclipse.editors.IconFactory;
 import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
@@ -26,6 +27,7 @@
 import com.android.ide.eclipse.editors.uimodel.UiDocumentNode;
 import com.android.ide.eclipse.editors.uimodel.UiElementNode;
 
+import org.eclipse.core.resources.IProject;
 import org.eclipse.jface.action.Action;
 import org.eclipse.jface.action.IMenuListener;
 import org.eclipse.jface.action.IMenuManager;
@@ -285,13 +287,21 @@
             }
         };
 
-        final Runnable resourceRefreshListener = new Runnable() {
-            public void run() {
+        /** Listener to update the root node if the target of the file is changed because of a
+         * SDK location change or a project target change */
+        final ITargetChangeListener targetListener = new ITargetChangeListener() {
+            public void onProjectTargetChange(IProject changedProject) {
+                if (changedProject == mEditor.getProject()) {
+                    onTargetsLoaded();
+                }
+            }
+
+            public void onTargetsLoaded() {
                 // If a details part has been created, we need to "refresh" it too.
                 if (mDetailsPart != null) {
                     // The details part does not directly expose access to its internal
                     // page book. Instead it is possible to resize the page book to 0 and then
-                    // back to its original value, which as the side effect of removing all
+                    // back to its original value, which has the side effect of removing all
                     // existing cached pages.
                     int limit = mDetailsPart.getPageLimit();
                     mDetailsPart.setPageLimit(0);
@@ -306,7 +316,7 @@
         changeRootAndDescriptors(mUiRootNode, mDescriptorFilters, false /* refresh */);
 
         // Listen on resource framework changes to refresh the tree
-        AdtPlugin.getDefault().addResourceChangedListener(resourceRefreshListener);
+        AdtPlugin.getDefault().addTargetListener(targetListener);
 
         // Remove listeners when the tree widget gets disposed.
         tree.addDisposeListener(new DisposeListener() {
@@ -318,7 +328,7 @@
                 node.removeUpdateListener(mUiRefreshListener);
                 mUiRootNode.removeUpdateListener(mUiEnableListener);
 
-                AdtPlugin.getDefault().removeResourceChangedListener(resourceRefreshListener);
+                AdtPlugin.getDefault().removeTargetListener(targetListener);
                 if (mClipboard != null) {
                     mClipboard.dispose();
                     mClipboard = null;
@@ -580,7 +590,11 @@
                         ui_node = ui_node.getUiParent()) {
                     segments.add(0, ui_node);
                 }
-                mTreeViewer.setSelection(new TreeSelection(new TreePath(segments.toArray())));
+                if (segments.size() > 0) {
+                    mTreeViewer.setSelection(new TreeSelection(new TreePath(segments.toArray())));
+                } else {
+                    mTreeViewer.setSelection(null);
+                }
             }
         }
 
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
index 4d17176..5781938 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/wizards/NewXmlFileCreationPage.java
@@ -23,6 +23,7 @@
 import com.android.ide.eclipse.common.project.ProjectChooserHelper;
 import com.android.ide.eclipse.editors.descriptors.DocumentDescriptor;
 import com.android.ide.eclipse.editors.descriptors.ElementDescriptor;
+import com.android.ide.eclipse.editors.descriptors.IDescriptorProvider;
 import com.android.ide.eclipse.editors.menu.descriptors.MenuDescriptors;
 import com.android.ide.eclipse.editors.resources.configurations.FolderConfiguration;
 import com.android.ide.eclipse.editors.resources.configurations.ResourceQualifier;
@@ -81,6 +82,7 @@
         private final String mXmlns;
         private final String mDefaultAttrs;
         private final String mDefaultRoot;
+        private final int mTargetApiLevel;
         
         public TypeInfo(String uiName,
                         String tooltip, 
@@ -88,7 +90,8 @@
                         Object rootSeed,
                         String defaultRoot,
                         String xmlns,
-                        String defaultAttrs) {
+                        String defaultAttrs,
+                        int targetApiLevel) {
             mUiName = uiName;
             mResFolderType = resFolderType;
             mTooltip = tooltip;
@@ -96,6 +99,7 @@
             mDefaultRoot = defaultRoot;
             mXmlns = xmlns;
             mDefaultAttrs = defaultAttrs;
+            mTargetApiLevel = targetApiLevel;
         }
 
         /** Returns the UI name for the resource type. Unique. Never null. */
@@ -176,6 +180,13 @@
         String getDefaultAttrs() {
             return mDefaultAttrs;
         }
+
+        /**
+         * The minimum API level required by the current SDK target to support this feature.
+         */
+        public int getTargetApiLevel() {
+            return mTargetApiLevel;
+        }
     }
 
     /**
@@ -190,7 +201,8 @@
                 "LinearLayout",                                     // default root
                 SdkConstants.NS_RESOURCES,                          // xmlns
                 "android:layout_width=\"wrap_content\"\n" +         // default attributes
-                "android:layout_height=\"wrap_content\""
+                "android:layout_height=\"wrap_content\"",
+                1                                                   // target API level
                 ),
         new TypeInfo("Values",                                      // UI name
                 "An XML file with simple values: colors, strings, dimensions, etc.", // tooltip
@@ -198,7 +210,8 @@
                 ResourcesDescriptors.ROOT_ELEMENT,                  // root seed
                 null,                                               // default root
                 null,                                               // xmlns
-                null                                                // default attributes
+                null,                                               // default attributes
+                1                                                   // target API level
                 ),
         new TypeInfo("Menu",                                        // UI name
                 "An XML file that describes an menu.",              // tooltip
@@ -206,7 +219,17 @@
                 MenuDescriptors.MENU_ROOT_ELEMENT,                  // root seed
                 null,                                               // default root
                 SdkConstants.NS_RESOURCES,                          // xmlns
-                null                                                // default attributes
+                null,                                               // default attributes
+                1                                                   // target API level
+                ),
+        new TypeInfo("Gadget Provider",                             // UI name
+                "An XML file that describes a gadget provider.",    // tooltip
+                ResourceFolderType.XML,                             // folder type
+                AndroidTargetData.DESCRIPTOR_GADGET_PROVIDER,       // root seed
+                null,                                               // default root
+                SdkConstants.NS_RESOURCES,                          // xmlns
+                null,                                               // default attributes
+                3                                                   // target API level
                 ),
         new TypeInfo("Preference",                                  // UI name
                 "An XML file that describes preferences.",          // tooltip
@@ -214,15 +237,17 @@
                 AndroidTargetData.DESCRIPTOR_PREFERENCES,           // root seed
                 AndroidConstants.CLASS_PREFERENCE_SCREEN,           // default root
                 SdkConstants.NS_RESOURCES,                          // xmlns
-                null                                                // default attributes
+                null,                                               // default attributes
+                1                                                   // target API level
                 ),
         new TypeInfo("Searchable",                                  // UI name
-                "An XML file that describes a searchable [TODO].",  // tooltip
+                "An XML file that describes a searchable.",         // tooltip
                 ResourceFolderType.XML,                             // folder type
                 AndroidTargetData.DESCRIPTOR_SEARCHABLE,            // root seed
                 null,                                               // default root
                 SdkConstants.NS_RESOURCES,                          // xmlns
-                null                                                // default attributes
+                null,                                               // default attributes
+                1                                                   // target API level
                 ),
         new TypeInfo("Animation",                                   // UI name
                 "An XML file that describes an animation.",         // tooltip
@@ -237,10 +262,14 @@
                     },
                 "set",              //$NON-NLS-1$                   // default root
                 null,                                               // xmlns
-                null                                                // default attributes
+                null,                                               // default attributes
+                1                                                   // target API level
                 ),
     };
 
+    /** Number of columns in the grid layout */
+    final static int NUM_COL = 4;
+
     /** Absolute destination folder root, e.g. "/res/" */
     private static String sResFolderAbs = AndroidConstants.WS_RESOURCES + AndroidConstants.WS_SEP;
     /** Relative destination folder root, e.g. "res/" */
@@ -290,7 +319,7 @@
 
         initializeDialogUnits(parent);
 
-        composite.setLayout(new GridLayout(3, false /*makeColumnsEqualWidth*/));
+        composite.setLayout(new GridLayout(NUM_COL, false /*makeColumnsEqualWidth*/));
         composite.setLayoutData(new GridData(GridData.FILL_BOTH));
 
         createProjectGroup(composite);
@@ -303,8 +332,9 @@
         setControl(composite);
 
         // Update state the first time
-        initializeRootValues();
         initializeFromSelection(mInitialSelection);
+        initializeRootValues();
+        enableTypesBasedOnApi();
         validatePage();
     }
 
@@ -419,16 +449,34 @@
     }
 
     /**
+     * Pads the parent with empty cells to match the number of columns of the parent grid.
+     * 
+     * @param parent A grid layout with NUM_COL columns
+     * @param col The current number of columns used.
+     * @return 0, the new number of columns used, for convenience.
+     */
+    private int padWithEmptyCells(Composite parent, int col) {
+        for (; col < NUM_COL; ++col) {
+            emptyCell(parent);
+        }
+        col = 0;
+        return col;
+    }
+
+    /**
      * Creates the project & filename fields.
      * <p/>
-     * The parent must be a GridLayout with 3 colums.
+     * The parent must be a GridLayout with NUM_COL colums.
      */
     private void createProjectGroup(Composite parent) {
+        int col = 0;
+        
         // project name
         String tooltip = "The Android Project where the new resource file will be created.";
         Label label = new Label(parent, SWT.NONE);
         label.setText("Project");
         label.setToolTipText(tooltip);
+        ++col;
 
         mProjectTextField = new Text(parent, SWT.BORDER);
         mProjectTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
@@ -438,6 +486,7 @@
                 onProjectFieldUpdated();
             }
         });
+        ++col;
 
         mProjectBrowseButton = new Button(parent, SWT.NONE);
         mProjectBrowseButton.setText("Browse...");
@@ -449,12 +498,16 @@
             }
         });
         mProjectChooserHelper = new ProjectChooserHelper(parent.getShell());
+        ++col;
 
+        col = padWithEmptyCells(parent, col);
+        
         // file name
         tooltip = "The name of the resource file to create.";
         label = new Label(parent, SWT.NONE);
         label.setText("File");
         label.setToolTipText(tooltip);
+        ++col;
 
         mFileNameTextField = new Text(parent, SWT.BORDER);
         mFileNameTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
@@ -464,31 +517,32 @@
                 validatePage();
             }
         });
+        ++col;
 
-        emptyCell(parent);
+        padWithEmptyCells(parent, col);
     }
 
     /**
      * Creates the type field, {@link ConfigurationSelector} and the folder field.
      * <p/>
-     * The parent must be a GridLayout with 3 colums.
+     * The parent must be a GridLayout with NUM_COL colums.
      */
     private void createTypeGroup(Composite parent) {
         // separator
         Label label = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
-        label.setLayoutData(newGridData(3, GridData.GRAB_HORIZONTAL));
+        label.setLayoutData(newGridData(NUM_COL, GridData.GRAB_HORIZONTAL));
         
         // label before type radios
         label = new Label(parent, SWT.NONE);
         label.setText("What type of resource would you like to create?");
-        label.setLayoutData(newGridData(3));
+        label.setLayoutData(newGridData(NUM_COL));
 
         // display the types on three columns of radio buttons.
         emptyCell(parent);
         Composite grid = new Composite(parent, SWT.NONE);
-        emptyCell(parent);
+        padWithEmptyCells(parent, 2);
 
-        grid.setLayout(new GridLayout(3, true /*makeColumnsEqualWidth*/));
+        grid.setLayout(new GridLayout(NUM_COL, true /*makeColumnsEqualWidth*/));
         
         SelectionListener radioListener = new SelectionAdapter() {
             @Override
@@ -501,23 +555,27 @@
         };
         
         int n = sTypes.length;
-        int num_lines = n/3;
-        for (int line = 0; line < num_lines; line++) {
-            for (int i = 0; i < 3; i++) {
-                TypeInfo type = sTypes[line * 3 + i];
-                Button radio = new Button(grid, SWT.RADIO);
-                type.setWidget(radio);
-                radio.setSelection(false);
-                radio.setText(type.getUiName());
-                radio.setToolTipText(type.getTooltip());
-                radio.addSelectionListener(radioListener);
+        int num_lines = (n + NUM_COL/2) / NUM_COL;
+        for (int line = 0, k = 0; line < num_lines; line++) {
+            for (int i = 0; i < NUM_COL; i++, k++) {
+                if (k < n) {
+                    TypeInfo type = sTypes[k];
+                    Button radio = new Button(grid, SWT.RADIO);
+                    type.setWidget(radio);
+                    radio.setSelection(false);
+                    radio.setText(type.getUiName());
+                    radio.setToolTipText(type.getTooltip());
+                    radio.addSelectionListener(radioListener);
+                } else {
+                    emptyCell(grid);
+                }
             }
         }
 
         // label before configuration selector
         label = new Label(parent, SWT.NONE);
         label.setText("What type of resource configuration would you like?");
-        label.setLayoutData(newGridData(3));
+        label.setLayoutData(newGridData(NUM_COL));
 
         // configuration selector
         emptyCell(parent);
@@ -527,6 +585,7 @@
         gd.heightHint = ConfigurationSelector.HEIGHT_HINT;
         mConfigSelector.setLayoutData(gd);
         mConfigSelector.setOnChangeListener(new onConfigSelectorUpdated());
+        emptyCell(parent);
         
         // folder name
         String tooltip = "The folder where the file will be generated, relative to the project.";
@@ -542,25 +601,23 @@
                 onWsFolderPathUpdated();
             }
         });
-
-        emptyCell(parent);
     }
 
     /**
      * Creates the root element combo.
      * <p/>
-     * The parent must be a GridLayout with 3 colums.
+     * The parent must be a GridLayout with NUM_COL colums.
      */
     private void createRootGroup(Composite parent) {
         // separator
         Label label = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
-        label.setLayoutData(newGridData(3, GridData.GRAB_HORIZONTAL));
+        label.setLayoutData(newGridData(NUM_COL, GridData.GRAB_HORIZONTAL));
 
         // label before the root combo
         String tooltip = "The root element to create in the XML file.";
         label = new Label(parent, SWT.NONE);
         label.setText("Select the root element for the XML file:");
-        label.setLayoutData(newGridData(3));
+        label.setLayoutData(newGridData(NUM_COL));
         label.setToolTipText(tooltip);
 
         // root combo
@@ -572,7 +629,7 @@
         mRootElementCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
         mRootElementCombo.setToolTipText(tooltip);
         
-        emptyCell(parent);
+        padWithEmptyCells(parent, 2);
     }
 
     /**
@@ -690,11 +747,13 @@
                 // get the AndroidTargetData from the project
                 IAndroidTarget target = Sdk.getCurrent().getTarget(mProject);
                 AndroidTargetData data = Sdk.getCurrent().getTargetData(target);
-                ElementDescriptor descriptor = data.getDescriptorProvider(
-                        (Integer)rootSeed).getDescriptor();
                 
-                HashSet<ElementDescriptor> visited = new HashSet<ElementDescriptor>();
-                initRootElementDescriptor(roots, descriptor, visited);
+                IDescriptorProvider provider = data.getDescriptorProvider((Integer)rootSeed);
+                ElementDescriptor descriptor = provider.getDescriptor();
+                if (descriptor != null) {
+                    HashSet<ElementDescriptor> visited = new HashSet<ElementDescriptor>();
+                    initRootElementDescriptor(roots, descriptor, visited);
+                }
 
                 // Sort alphabetically.
                 Collections.sort(roots);
@@ -743,15 +802,7 @@
         }
 
         if (found != mProject) {
-            mProject = found;
-
-            // update the Type with the new descriptors.
-            initializeRootValues();
-            
-            // update the combo
-            updateRootCombo(getSelectedType());
-            
-            validatePage();
+            changeProject(found);
         }
     }
 
@@ -761,17 +812,27 @@
     private void onProjectBrowse() {
         IJavaProject p = mProjectChooserHelper.chooseJavaProject(mProjectTextField.getText());
         if (p != null) {
-            mProject = p.getProject();
+            changeProject(p.getProject());
             mProjectTextField.setText(mProject.getName());
-            
-            // update the Type with the new descriptors.
-            initializeRootValues();
-            
-            // update the combo
-            updateRootCombo(getSelectedType());
-            
-            validatePage();
         }
+    }
+
+    /**
+     * Changes mProject to the given new project and update the UI accordingly.
+     */
+    private void changeProject(IProject newProject) {
+        mProject = newProject;
+
+        // enable types based on new API level
+        enableTypesBasedOnApi();
+        
+        // update the Type with the new descriptors.
+        initializeRootValues();
+        
+        // update the combo
+        updateRootCombo(getSelectedType());
+        
+        validatePage();
     } 
 
     /**
@@ -986,6 +1047,26 @@
     }
 
     /**
+     * Helper method to enable the type radio buttons depending on the current API level.
+     * <p/>
+     * A type radio button is enabled either if:
+     * - if mProject is null, API level 1 is considered valid
+     * - if mProject is !null, the project->target->API must be >= to the type's API level.
+     */
+    private void enableTypesBasedOnApi() {
+
+        IAndroidTarget target = mProject != null ? Sdk.getCurrent().getTarget(mProject) : null;
+        int currentApiLevel = 1;
+        if (target != null) {
+            currentApiLevel = target.getApiVersionNumber();
+        }
+        
+        for (TypeInfo type : sTypes) {
+            type.getWidget().setEnabled(type.getTargetApiLevel() <= currentApiLevel);
+        }
+    }
+
+    /**
      * Validates the fields, displays errors and warnings.
      * Enables the finish button if there are no errors.
      */
@@ -1017,6 +1098,22 @@
             }
         }
 
+        // -- validate type API level
+        if (error == null) {
+            IAndroidTarget target = Sdk.getCurrent().getTarget(mProject);
+            int currentApiLevel = 1;
+            if (target != null) {
+                currentApiLevel = target.getApiVersionNumber();
+            }
+
+            TypeInfo type = getSelectedType();
+
+            if (type.getTargetApiLevel() > currentApiLevel) {
+                error = "The API level of the selected type (e.g. gadget, etc.) is not " +
+                        "compatible with the API level of the project.";
+            }
+        }
+
         // -- validate folder configuration
         if (error == null) {
             ConfigurationState state = mConfigSelector.getState();
diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/descriptors/XmlDescriptors.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/descriptors/XmlDescriptors.java
index fa1370f..7929b5a 100644
--- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/descriptors/XmlDescriptors.java
+++ b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/editors/xml/descriptors/XmlDescriptors.java
@@ -53,6 +53,9 @@
     /** The root document descriptor for preferences. */
     private DocumentDescriptor mPrefDescriptor = new DocumentDescriptor("xml_doc", null /* children */); //$NON-NLS-1$ 
 
+    /** The root document descriptor for gadget provider. */
+    private DocumentDescriptor mGadgetDescriptor = new DocumentDescriptor("xml_doc", null /* children */); //$NON-NLS-1$ 
+
     /** @return the root descriptor for both searchable and preferences. */
     public DocumentDescriptor getDescriptor() {
         return mDescriptor;
@@ -72,6 +75,11 @@
         return mPrefDescriptor;
     }
     
+    /** @return the root descriptor for gadget providers. */
+    public DocumentDescriptor getGadgetDescriptor() {
+        return mGadgetDescriptor;
+    }
+    
     public IDescriptorProvider getSearchableProvider() {
         return new IDescriptorProvider() {
             public ElementDescriptor getDescriptor() {
@@ -96,6 +104,18 @@
         };
     }
 
+    public IDescriptorProvider getGadgetProvider() {
+        return new IDescriptorProvider() {
+            public ElementDescriptor getDescriptor() {
+                return mGadgetDescriptor;
+            }
+
+            public ElementDescriptor[] getRootElementDescriptors() {
+                return mGadgetDescriptor.getChildren();
+            }
+        };
+    }
+
     /**
      * Updates the document descriptor.
      * <p/>
@@ -103,11 +123,13 @@
      * all at once.
      * 
      * @param searchableStyleMap The map style=>attributes for <searchable> from the attrs.xml file
+     * @param gadgetStyleMap The map style=>attributes for <gadget-provider> from the attrs.xml file
      * @param prefs The list of non-group preference descriptions 
      * @param prefGroups The list of preference group descriptions
      */
     public synchronized void updateDescriptors(
             Map<String, DeclareStyleableInfo> searchableStyleMap,
+            Map<String, DeclareStyleableInfo> gadgetStyleMap,
             ViewClassInfo[] prefs, ViewClassInfo[] prefGroups) {
 
         XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(
@@ -115,12 +137,17 @@
                 SdkConstants.NS_RESOURCES); 
 
         ElementDescriptor searchable = createSearchable(searchableStyleMap, xmlns);
+        ElementDescriptor gadget = createGadgetProviderInfo(gadgetStyleMap, xmlns);
         ElementDescriptor preferences = createPreference(prefs, prefGroups, xmlns);
         ArrayList<ElementDescriptor> list =  new ArrayList<ElementDescriptor>();
         if (searchable != null) {
             list.add(searchable);
             mSearchDescriptor.setChildren(new ElementDescriptor[]{ searchable });
         }
+        if (gadget != null) {
+            list.add(gadget);
+            mGadgetDescriptor.setChildren(new ElementDescriptor[]{ gadget });
+        }
         if (preferences != null) {
             list.add(preferences);
             mPrefDescriptor.setChildren(new ElementDescriptor[]{ preferences });
@@ -161,6 +188,28 @@
                 false /* mandatory */ );
         return searchable;
     }
+    
+    /**
+     * Returns the new ElementDescriptor for <gadget-provider>
+     */
+    private ElementDescriptor createGadgetProviderInfo(
+            Map<String, DeclareStyleableInfo> gadgetStyleMap,
+            XmlnsAttributeDescriptor xmlns) {
+
+        if (gadgetStyleMap == null) {
+            return null;
+        }
+        
+        ElementDescriptor gadget = createElement(gadgetStyleMap,
+                "GadgetProviderInfo", //$NON-NLS-1$ styleName
+                "gadget-provider", //$NON-NLS-1$ xmlName
+                "Gadget Provider", // uiName
+                null, // sdk url
+                xmlns, // extraAttribute
+                null, // childrenElements
+                false /* mandatory */ );
+        return gadget;
+    }
 
     /**
      * Returns a new ElementDescriptor constructed from the information given here
diff --git a/tools/eclipse/scripts/collect_sources_for_sdk.sh b/tools/eclipse/scripts/collect_sources_for_sdk.sh
index 4637595..4824da7 100644
--- a/tools/eclipse/scripts/collect_sources_for_sdk.sh
+++ b/tools/eclipse/scripts/collect_sources_for_sdk.sh
@@ -22,6 +22,13 @@
     shift
 fi
 
+DIR="frameworks"
+if [ "-s" == "$1" ]; then
+    shift
+    DIR="$1"
+    shift
+fi
+
 SRC="$1"
 DST="$2"
 
@@ -36,7 +43,7 @@
 
 N=0
 E=0
-for i in `find -L "${SRC}/frameworks" -name "*.java"`; do
+for i in `find -L "${SRC}/${DIR}" -name "*.java"`; do
     if [ -f "$i" ]; then
         # look for ^package (android.view.blah);$
         PACKAGE=`sed -n '/^package [^ ;]\+; */{s/[^ ]* *\([^ ;]*\).*/\1/p;q}' "$i"`
diff --git a/tools/hierarchyviewer/etc/hierarchyviewer.bat b/tools/hierarchyviewer/etc/hierarchyviewer.bat
index 67e4f80..2024a79 100755
--- a/tools/hierarchyviewer/etc/hierarchyviewer.bat
+++ b/tools/hierarchyviewer/etc/hierarchyviewer.bat
@@ -20,9 +20,9 @@
 rem and set up progdir to be the fully-qualified pathname of its directory.
 set prog=%~f0
 
-rem Change current directory to where ddms is, to avoid issues with directories
-rem containing whitespaces.
-cd %~dp0
+rem Change current directory and drive to where the script is, to avoid
+rem issues with directories containing whitespaces.
+cd /d %~dp0
 
 set jarfile=hierarchyviewer.jar
 set frameworkdir=
diff --git a/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewHierarchyLoader.java b/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewHierarchyLoader.java
index 6efb52d..51e1396 100644
--- a/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewHierarchyLoader.java
+++ b/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewHierarchyLoader.java
@@ -32,6 +32,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Stack;
+import java.util.regex.Pattern;
 
 public class ViewHierarchyLoader {
     @SuppressWarnings("empty-statement")
@@ -109,7 +110,9 @@
                     parent.children.add(lastNode);
                 }
             }
-            
+
+            updateIndices(scene.getRoot());
+
         } catch (IOException ex) {
             Exceptions.printStackTrace(ex);
         } finally {
@@ -127,10 +130,18 @@
         }
         
         System.out.println("==> DONE");
-        
+
         return scene;
     }
-    
+
+    private static void updateIndices(ViewNode root) {
+        root.computeIndex();
+
+        for (ViewNode node : root.children) {
+            updateIndices(node);
+        }
+    }
+
     private static int countFrontWhitespace(String line) {
         int count = 0;
         while (line.charAt(count) == ' ') {
diff --git a/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewHierarchyScene.java b/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewHierarchyScene.java
index d99a80c..08dc395 100644
--- a/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewHierarchyScene.java
+++ b/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewHierarchyScene.java
@@ -60,22 +60,25 @@
 
     @Override
     protected Widget attachNodeWidget(ViewNode node) {
-        Widget widget = createBox(node.name, node.id);
+        Widget widget = createBox(node, node.name, node.id);
         widget.getActions().addAction(createSelectAction());
         widget.getActions().addAction(moveAction);
         widgetLayer.addChild(widget);
         return widget;
     }
 
-    private Widget createBox(String node, String id) {
-        Widget box = new GradientWidget(this);
+    private Widget createBox(ViewNode node, String nodeName, String id) {
+        final String shortName = getShortName(nodeName);
+        node.setShortName(shortName);
+
+        GradientWidget box = new GradientWidget(this, node);
         box.setLayout(LayoutFactory.createVerticalFlowLayout());
         box.setBorder(BorderFactory.createLineBorder(2, Color.BLACK));
         box.setOpaque(true);
 
         LabelWidget label = new LabelWidget(this);
         label.setFont(getDefaultFont().deriveFont(Font.PLAIN, 12.0f));
-        label.setLabel(getShortName(node));
+        label.setLabel(shortName);
         label.setBorder(BorderFactory.createEmptyBorder(6, 6, 0, 6));
         label.setAlignment(LabelWidget.Alignment.CENTER);
 
@@ -83,9 +86,11 @@
         
         label = new LabelWidget(this);
         label.setFont(getDefaultFont().deriveFont(Font.PLAIN, 10.0f));
-        label.setLabel(getAddress(node));
+        label.setLabel(getAddress(nodeName));
         label.setBorder(BorderFactory.createEmptyBorder(3, 6, 0, 6));
         label.setAlignment(LabelWidget.Alignment.CENTER);
+
+        box.addressWidget = label;
         
         box.addChild(label);
         
@@ -136,7 +141,7 @@
         connection.setTargetAnchor(AnchorFactory.createRectangularAnchor(target));
     }
     
-    private static class GradientWidget extends Widget {
+    private static class GradientWidget extends Widget implements ViewNode.StateListener {
         public static final GradientPaint BLUE_EXPERIENCE = new GradientPaint(
                 new Point2D.Double(0, 0),
                 new Color(168, 204, 241),
@@ -177,15 +182,28 @@
                 new Color(129, 138, 155),
                 new Point2D.Double(0, 1),
                 new Color(58, 66, 82));
+        public static final GradientPaint NIGHT_GRAY_VERY_LIGHT = new GradientPaint(
+                new Point2D.Double(0, 0),
+                new Color(129, 138, 155, 60),
+                new Point2D.Double(0, 1),
+                new Color(58, 66, 82, 60));
 
         private static Color UNSELECTED = Color.BLACK;
         private static Color SELECTED = Color.WHITE;
 
-        private boolean isSelected = false;
-        private GradientPaint gradient = MAC_OSX_SELECTED;
+        private final ViewNode node;
 
-        public GradientWidget(ViewHierarchyScene scene) {
+        private LabelWidget addressWidget;
+
+        private boolean isSelected = false;
+        private final GradientPaint selectedGradient = MAC_OSX_SELECTED;
+        private final GradientPaint filteredGradient = RED_XP;
+        private final GradientPaint focusGradient = NIGHT_GRAY_VERY_LIGHT;
+
+        public GradientWidget(ViewHierarchyScene scene, ViewNode node) {
             super(scene);
+            this.node = node;
+            node.setStateListener(this);
         }
 
         @Override
@@ -193,8 +211,12 @@
             super.notifyStateChanged(previous, state);
             isSelected = state.isSelected() || state.isFocused() || state.isWidgetFocused();
 
+            pickChildrenColor();
+        }
+
+        private void pickChildrenColor() {
             for (Widget child : getChildren()) {
-                child.setForeground(isSelected ? SELECTED : UNSELECTED);
+                child.setForeground(isSelected || node.filtered ? SELECTED : UNSELECTED);
             }
 
             repaint();
@@ -206,14 +228,35 @@
 
             Graphics2D g2 = getGraphics();
             Rectangle bounds = getBounds();
-            
+
             if (!isSelected) {
-                g2.setColor(Color.WHITE);
+                if (!node.filtered) {
+                    if (!node.hasFocus) {
+                        g2.setColor(Color.WHITE);
+                    } else {
+                        g2.setPaint(new GradientPaint(bounds.x, bounds.y,
+                                focusGradient.getColor1(), bounds.x, bounds.x + bounds.height,
+                                focusGradient.getColor2()));
+                    }
+                } else {
+                    g2.setPaint(new GradientPaint(bounds.x, bounds.y, filteredGradient.getColor1(),
+                        bounds.x, bounds.x + bounds.height, filteredGradient.getColor2()));
+                }
             } else {
-                g2.setPaint(new GradientPaint(bounds.x, bounds.y, gradient.getColor1(),
-                        bounds.x, bounds.x + bounds.height, gradient.getColor2()));
+                g2.setPaint(new GradientPaint(bounds.x, bounds.y, selectedGradient.getColor1(),
+                        bounds.x, bounds.x + bounds.height, selectedGradient.getColor2()));
             }
             g2.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
         }
+
+        public void nodeStateChanged(ViewNode node) {
+            pickChildrenColor();
+        }
+
+        public void nodeIndexChanged(ViewNode node) {
+            if (addressWidget != null) {
+                addressWidget.setLabel("#" + node.index + addressWidget.getLabel());
+            }
+        }
     }
 }
diff --git a/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewManager.java b/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewManager.java
index 6b212c0..2b7efd6 100644
--- a/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewManager.java
+++ b/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewManager.java
@@ -17,7 +17,6 @@
 package com.android.hierarchyviewer.scene;
 
 import com.android.ddmlib.Device;
-import com.android.hierarchyviewer.device.Configuration;
 import com.android.hierarchyviewer.device.Window;
 import com.android.hierarchyviewer.device.DeviceBridge;
 
diff --git a/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewNode.java b/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewNode.java
index 8284df1..64c0703 100644
--- a/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewNode.java
+++ b/tools/hierarchyviewer/src/com/android/hierarchyviewer/scene/ViewNode.java
@@ -21,6 +21,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.regex.Pattern;
 
 public class ViewNode {
     public String id;
@@ -52,8 +53,15 @@
     public boolean willNotDraw;
     public boolean hasMargins;
     
+    boolean hasFocus;
+    int index;
+
     public boolean decoded;
-    
+    public boolean filtered;
+
+    private String shortName;
+    private StateListener listener;
+
     void decode() {
         id = namedProperties.get("mID").value;
 
@@ -73,6 +81,7 @@
         marginBottom = getInt("layout_bottomMargin", Integer.MIN_VALUE);
         baseline = getInt("getBaseline()", 0);
         willNotDraw = getBoolean("willNotDraw()", false);
+        hasFocus = getBoolean("hasFocus()", false);
 
         hasMargins = marginLeft != Integer.MIN_VALUE &&
                 marginRight != Integer.MIN_VALUE &&
@@ -101,11 +110,33 @@
                 return Integer.parseInt(p.value);
             } catch (NumberFormatException e) {
                 return defaultValue;
-            }   
+            }
         }
         return defaultValue;
     }
 
+    public void filter(Pattern pattern) {
+        if (pattern == null || pattern.pattern().length() == 0) {
+            filtered = false;
+        } else {
+            filtered = pattern.matcher(shortName).find() || pattern.matcher(id).find();
+        }
+        listener.nodeStateChanged(this);
+    }
+
+    void computeIndex() {
+        index = parent == null ? 0 : parent.children.indexOf(this);
+        listener.nodeIndexChanged(this);
+    }
+
+    void setShortName(String shortName) {
+        this.shortName = shortName;
+    }
+
+    void setStateListener(StateListener listener) {
+        this.listener = listener;
+    }
+
     @SuppressWarnings({"StringEquality"})
     @Override
     public boolean equals(Object obj) {
@@ -164,4 +195,9 @@
             return hash;
         }
     }
+
+    interface StateListener {
+        void nodeStateChanged(ViewNode node);
+        void nodeIndexChanged(ViewNode node);
+    }
 }
diff --git a/tools/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java b/tools/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java
index 0add4e9..20093ae 100644
--- a/tools/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java
+++ b/tools/hierarchyviewer/src/com/android/hierarchyviewer/ui/Workspace.java
@@ -76,6 +76,9 @@
 import javax.swing.SwingUtilities;
 import javax.swing.JTree;
 import javax.swing.Box;
+import javax.swing.JTextField;
+import javax.swing.text.Document;
+import javax.swing.text.BadLocationException;
 import javax.swing.tree.TreePath;
 import javax.swing.tree.DefaultTreeCellRenderer;
 import javax.swing.event.ChangeEvent;
@@ -84,6 +87,8 @@
 import javax.swing.event.ListSelectionListener;
 import javax.swing.event.TreeSelectionListener;
 import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.DocumentEvent;
 import javax.swing.table.DefaultTableModel;
 import java.awt.image.BufferedImage;
 import java.awt.BorderLayout;
@@ -105,6 +110,8 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
 import java.util.concurrent.ExecutionException;
 
 public class Workspace extends JFrame {
@@ -156,6 +163,8 @@
     private JTable windows;
     private JLabel minZoomLabel;
     private JLabel maxZoomLabel;
+    private JTextField filterText;
+    private JLabel filterLabel;
 
     public Workspace() {
         super("Hierarchy Viewer");
@@ -313,10 +322,33 @@
 
         graphViewButton.setSelected(true);
 
+        filterText = new JTextField(20);
+        filterText.putClientProperty("JComponent.sizeVariant", "small");
+        filterText.getDocument().addDocumentListener(new DocumentListener() {
+            public void insertUpdate(DocumentEvent e) {
+                updateFilter(e);
+            }
+
+            public void removeUpdate(DocumentEvent e) {
+                updateFilter(e);
+            }
+
+            public void changedUpdate(DocumentEvent e) {
+                updateFilter(e);
+            }
+        });
+
+        filterLabel = new JLabel("Filter by class or id:");
+        filterLabel.putClientProperty("JComponent.sizeVariant", "small");
+        filterLabel.setBorder(BorderFactory.createEmptyBorder(0, 6, 0, 6));
+
+        leftSide.add(filterLabel);
+        leftSide.add(filterText);
+
         minZoomLabel = new JLabel();
         minZoomLabel.setText("20%");
         minZoomLabel.putClientProperty("JComponent.sizeVariant", "small");
-        minZoomLabel.setBorder(BorderFactory.createEmptyBorder(0, 6, 0, 0));
+        minZoomLabel.setBorder(BorderFactory.createEmptyBorder(0, 12, 0, 0));
         leftSide.add(minZoomLabel);
 
         zoomSlider = new JSlider();
@@ -357,12 +389,18 @@
 
         statusPanel.add(rightSide, BorderLayout.LINE_END);
 
+        hideStatusBarComponents();
+
+        return statusPanel;
+    }
+
+    private void hideStatusBarComponents() {
         viewCountLabel.setVisible(false);
         zoomSlider.setVisible(false);
         minZoomLabel.setVisible(false);
-        maxZoomLabel.setVisible(false);        
-
-        return statusPanel;
+        maxZoomLabel.setVisible(false);
+        filterLabel.setVisible(false);
+        filterText.setVisible(false);
     }
 
     private JToolBar buildToolBar() {
@@ -513,10 +551,7 @@
     }
 
     private void toggleGraphView() {
-        viewCountLabel.setVisible(true);
-        zoomSlider.setVisible(true);
-        minZoomLabel.setVisible(true);
-        maxZoomLabel.setVisible(true);
+        showStatusBarComponents();
 
         screenViewer.stop();
         mainPanel.remove(pixelPerfectPanel);
@@ -526,6 +561,15 @@
         repaint();
     }
 
+    private void showStatusBarComponents() {
+        viewCountLabel.setVisible(true);
+        zoomSlider.setVisible(true);
+        minZoomLabel.setVisible(true);
+        maxZoomLabel.setVisible(true);
+        filterLabel.setVisible(true);
+        filterText.setVisible(true);
+    }
+
     private void togglePixelPerfectView() {
         if (pixelPerfectPanel == null) {
             pixelPerfectPanel = buildPixelPerfectPanel();
@@ -534,10 +578,7 @@
             screenViewer.start();
         }
 
-        viewCountLabel.setVisible(false);
-        zoomSlider.setVisible(false);
-        minZoomLabel.setVisible(false);
-        maxZoomLabel.setVisible(false);
+        hideStatusBarComponents();
 
         mainPanel.remove(mainSplitter);
         mainPanel.add(pixelPerfectPanel, BorderLayout.CENTER);
@@ -602,10 +643,7 @@
             graphViewButton.setEnabled(true);
             pixelPerfectViewButton.setEnabled(true);
 
-            viewCountLabel.setVisible(true);
-            zoomSlider.setVisible(true);
-            minZoomLabel.setVisible(true);
-            maxZoomLabel.setVisible(true);            
+            showStatusBarComponents();
         }
 
         sceneView = scene.createView();
@@ -776,10 +814,7 @@
             pixelPerfectPanel = mainSplitter = null;
             graphViewButton.setSelected(true);
 
-            viewCountLabel.setVisible(false);
-            zoomSlider.setVisible(false);
-            minZoomLabel.setVisible(false);
-            maxZoomLabel.setVisible(false);
+            hideStatusBarComponents();
 
             saveMenuItem.setEnabled(false);            
             showDevicesMenuItem.setEnabled(false);
@@ -865,6 +900,34 @@
         });
     }
 
+    private void updateFilter(DocumentEvent e) {
+        final Document document = e.getDocument();
+        try {
+            updateFilteredNodes(document.getText(0, document.getLength()));
+        } catch (BadLocationException e1) {
+            e1.printStackTrace();
+        }
+    }
+
+    private void updateFilteredNodes(String filterText) {
+        final ViewNode root = scene.getRoot();
+        try {
+            final Pattern pattern = Pattern.compile(filterText, Pattern.CASE_INSENSITIVE);
+            filterNodes(pattern, root);
+        } catch (PatternSyntaxException e) {
+            filterNodes(null, root);
+        }
+        repaint();
+    }
+
+    private void filterNodes(Pattern pattern, ViewNode root) {
+        root.filter(pattern);
+
+        for (ViewNode node : root.children) {
+            filterNodes(pattern, node);
+        }
+    }
+
     public void beginTask() {
         progress.setVisible(true);
     }
diff --git a/tools/runtest b/tools/runtest
index 6280910..349b5a7 100755
--- a/tools/runtest
+++ b/tools/runtest
@@ -113,7 +113,7 @@
 
   # system-wide tests
   "framework  frameworks/base/tests/FrameworkTest # com.android.frameworktest.AllTests com.android.frameworktest.tests #"
-  "android    frameworks/base/tests/AndroidTests  com.android.unit_tests AndroidTests # #"
+  "android    frameworks/base/tests/AndroidTests  # AndroidTests com.android.unit_tests #"
   "smoke      frameworks/base/tests/SmokeTest     com.android.smoketest # com.android.smoketest.tests #"
   "core       frameworks/base/tests/CoreTests     # android.core.CoreTests android.core #"
   "libcore    frameworks/base/tests/CoreTests     # android.core.JavaTests android.core #"
diff --git a/tools/sdkmanager/app/etc/android.bat b/tools/sdkmanager/app/etc/android.bat
index 1af1e47..de950ed 100755
--- a/tools/sdkmanager/app/etc/android.bat
+++ b/tools/sdkmanager/app/etc/android.bat
@@ -23,9 +23,9 @@
 rem Grab current directory before we change it
 set workdir=%cd%
 
-rem Change current directory to where ddms is, to avoid issues with directories
-rem containing whitespaces.
-cd %~dp0
+rem Change current directory and drive to where the script is, to avoid
+rem issues with directories containing whitespaces.
+cd /d %~dp0
 
 set jarfile=sdkmanager.jar
 set frameworkdir=
diff --git a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
index 1a15fce..154788e 100644
--- a/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
+++ b/tools/sdkmanager/app/src/com/android/sdkmanager/Main.java
@@ -383,24 +383,28 @@
      */
     private void displayAvdList() {
         try {
-            AvdManager avdManager = new AvdManager(mSdkManager, null /* sdklog */);
+            AvdManager avdManager = new AvdManager(mSdkManager, mSdkLog);
 
             mSdkLog.printf("Available Android Virtual Devices:\n");
 
-            int index = 1;
-            for (AvdInfo info : avdManager.getAvds()) {
-                mSdkLog.printf("[%d] %s\n", index, info.getName());
-                mSdkLog.printf("      Path: %s\n", info.getPath());
+            AvdInfo[] avds = avdManager.getAvds();
+            for (int index = 0 ; index < avds.length ; index++) {
+                AvdInfo info = avds[index];
+                if (index > 0) {
+                    mSdkLog.printf("---------\n");
+                }
+                mSdkLog.printf("    Name: %s\n", info.getName());
+                mSdkLog.printf("    Path: %s\n", info.getPath());
 
                 // get the target of the AVD
                 IAndroidTarget target = info.getTarget();
                 if (target.isPlatform()) {
-                    mSdkLog.printf("    Target: %s (API level %d)\n", target.getName(),
+                    mSdkLog.printf("  Target: %s (API level %d)\n", target.getName(),
                             target.getApiVersionNumber());
                 } else {
-                    mSdkLog.printf("    Target: %s (%s)\n", target.getName(), target
+                    mSdkLog.printf("  Target: %s (%s)\n", target.getName(), target
                             .getVendor());
-                    mSdkLog.printf("            Based on Android %s (API level %d)\n", target
+                    mSdkLog.printf("          Based on Android %s (API level %d)\n", target
                             .getApiVersionName(), target.getApiVersionNumber());
                 }
                 
@@ -408,17 +412,15 @@
                 Map<String, String> properties = info.getProperties();
                 String skin = properties.get(AvdManager.AVD_INI_SKIN_NAME);
                 if (skin != null) {
-                    mSdkLog.printf("      Skin: %s\n", skin);
+                    mSdkLog.printf("    Skin: %s\n", skin);
                 }
                 String sdcard = properties.get(AvdManager.AVD_INI_SDCARD_SIZE);
                 if (sdcard == null) {
                     sdcard = properties.get(AvdManager.AVD_INI_SDCARD_PATH);
                 }
                 if (sdcard != null) {
-                    mSdkLog.printf("    Sdcard: %s\n", sdcard);
+                    mSdkLog.printf("  Sdcard: %s\n", sdcard);
                 }
-
-                index++;
             }
         } catch (AndroidLocationException e) {
             errorAndExit(e.getMessage());
@@ -499,7 +501,7 @@
                 
                 // Is it NNNxMMM?
                 if (!valid) {
-                    valid = skin.matches("[0-9]{2,}x[0-9]{2,}");
+                    valid = AvdManager.NUMERIC_SKIN_SIZE.matcher(skin).matches();
                 }
 
                 if (!valid) {
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
index 87f9b56..00594d1 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/SdkConstants.java
@@ -103,6 +103,8 @@
     public final static String FD_ASSETS = "assets"; //$NON-NLS-1$
     /** Default source folder name, i.e. "src" */
     public final static String FD_SOURCES = "src"; //$NON-NLS-1$
+    /** Default generated source folder name, i.e. "gen" */
+    public final static String FD_GEN_SOURCES = "gen"; //$NON-NLS-1$
     /** Default native library folder name inside the project, i.e. "libs"
      * While the folder inside the .apk is "lib", we call that one libs because
      * that's what we use in ant for both .jar and .so and we need to make the 2 development ways
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
index b44cf01..2c0f164 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/avd/AvdManager.java
@@ -55,6 +55,12 @@
     public final static String AVD_INI_IMAGES_1 = "image.sysdir.1";
     public final static String AVD_INI_IMAGES_2 = "image.sysdir.2";
 
+    /**
+     * Pattern to match pixel-sized skin "names", e.g. "320x480".
+     */
+    public final static Pattern NUMERIC_SKIN_SIZE = Pattern.compile("[0-9]{2,}x[0-9]{2,}");
+
+    
     private final static String USERDATA_IMG = "userdata.img";
     private final static String CONFIG_INI = "config.ini";
     private final static String SDCARD_IMG = "sdcard.img";
@@ -255,16 +261,21 @@
                 skinName = target.getDefaultSkin();
             }
 
-            // get the path of the skin (relative to the SDK)
-            // assume skin name is valid
-            String skinPath = getSkinRelativePath(skinName, target, log);
-            if (skinPath == null) {
-                needCleanup = true;
-                return null;
-            }
+            if (NUMERIC_SKIN_SIZE.matcher(skinName).matches()) {
+                // Skin name is an actual screen resolution, no skin.path
+                values.put(AVD_INI_SKIN_NAME, skinName);
+            } else {
+                // get the path of the skin (relative to the SDK)
+                // assume skin name is valid
+                String skinPath = getSkinRelativePath(skinName, target, log);
+                if (skinPath == null) {
+                    needCleanup = true;
+                    return null;
+                }
 
-            values.put(AVD_INI_SKIN_PATH, skinPath);
-            values.put(AVD_INI_SKIN_NAME, skinName);
+                values.put(AVD_INI_SKIN_PATH, skinPath);
+                values.put(AVD_INI_SKIN_NAME, skinName);
+            }
 
             if (sdcard != null) {
                 File sdcardFile = new File(sdcard);
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ApkConfigurationHelper.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ApkConfigurationHelper.java
index ab43f46..b89d3bd 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ApkConfigurationHelper.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ApkConfigurationHelper.java
@@ -25,24 +25,30 @@
  * Helper class to read and write Apk Configuration into a {@link ProjectProperties} file.
  */
 public class ApkConfigurationHelper {
+    /** Prefix for property names for config definition. This prevents having config named
+     * after other valid properties such as "target". */
+    final static String CONFIG_PREFIX = "apk-config-";
 
     /**
      * Reads the Apk Configurations from a {@link ProjectProperties} file and returns them as a map.
      * <p/>If there are no defined configurations, the returned map will be empty.
+     * @return a map of apk configurations. The map contains (name, filter) where name is
+     * the name of the configuration (a-zA-Z0-9 only), and filter is the comma separated list of
+     * resource configuration to include in the apk (see aapt -c) 
      */
     public static Map<String, String> getConfigs(ProjectProperties properties) {
         HashMap<String, String> configMap = new HashMap<String, String>();
 
         // get the list of configs.
-        String configList = properties.getProperty(ProjectProperties.PROPERTY_CONFIGS);
+        String configList = properties.getProperty(ProjectProperties.PROPERTY_APK_CONFIGS);
         if (configList != null) {
             // this is a comma separated list
             String[] configs = configList.split(","); //$NON-NLS-1$
             
             // read the value of each config and store it in a map
-            
             for (String config : configs) {
-                String configValue = properties.getProperty(config);
+                config = config.trim();
+                String configValue = properties.getProperty(CONFIG_PREFIX + config);
                 if (configValue != null) {
                     configMap.put(config, configValue);
                 }
@@ -54,6 +60,10 @@
     
     /**
      * Writes the Apk Configurations from a given map into a {@link ProjectProperties}.
+     * @param properties the {@link ProjectProperties} in which to store the apk configurations. 
+     * @param configMap a map of apk configurations. The map contains (name, filter) where name is
+     * the name of the configuration (a-zA-Z0-9 only), and filter is the comma separated list of
+     * resource configuration to include in the apk (see aapt -c) 
      * @return true if the {@link ProjectProperties} contained Apk Configuration that were not
      * present in the map. 
      */
@@ -62,17 +72,20 @@
         // in case a config was removed.
         
         // get the list of configs.
-        String configList = properties.getProperty(ProjectProperties.PROPERTY_CONFIGS);
-        
-        // this is a comma separated list
-        String[] configs = configList.split(","); //$NON-NLS-1$
-        
+        String configList = properties.getProperty(ProjectProperties.PROPERTY_APK_CONFIGS);
+
         boolean hasRemovedConfig = false;
-        
-        for (String config : configs) {
-            if (configMap.containsKey(config) == false) {
-                hasRemovedConfig = true;
-                properties.removeProperty(config);
+
+        if (configList != null) {
+            // this is a comma separated list
+            String[] configs = configList.split(","); //$NON-NLS-1$
+            
+            for (String config : configs) {
+                config = config.trim();
+                if (configMap.containsKey(config) == false) {
+                    hasRemovedConfig = true;
+                    properties.removeProperty(CONFIG_PREFIX + config);
+                }
             }
         }
         
@@ -84,9 +97,9 @@
                 sb.append(",");
             }
             sb.append(entry.getKey());
-            properties.setProperty(entry.getKey(), entry.getValue());
+            properties.setProperty(CONFIG_PREFIX + entry.getKey(), entry.getValue());
         }
-        properties.setProperty(ProjectProperties.PROPERTY_CONFIGS, sb.toString());
+        properties.setProperty(ProjectProperties.PROPERTY_APK_CONFIGS, sb.toString());
         
         return hasRemovedConfig;
     }
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java
index 18e2ac9..7489b65 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectCreator.java
@@ -209,7 +209,7 @@
             }
             
             // create the source folder and the java package folders.
-            final String srcFolderPath = SdkConstants.FD_SOURCES + File.separator + packagePath;
+            String srcFolderPath = SdkConstants.FD_SOURCES + File.separator + packagePath;
             File sourceFolder = createDirs(projectFolder, srcFolderPath);
             String javaTemplate = "java_file.template";
             String activityFileName = activityName + ".java";
@@ -220,6 +220,10 @@
             installTemplate(javaTemplate, new File(sourceFolder, activityFileName),
                     keywords, target);
 
+            // create the generate source folder
+            srcFolderPath = SdkConstants.FD_GEN_SOURCES + File.separator + packagePath;
+            sourceFolder = createDirs(projectFolder, srcFolderPath);
+
             // create other useful folders
             File resourceFodler = createDirs(projectFolder, SdkConstants.FD_RESOURCES);
             createDirs(projectFolder, SdkConstants.FD_OUTPUT);
diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java
index 1f6a047..69a16be 100644
--- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java
+++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/project/ProjectProperties.java
@@ -33,7 +33,7 @@
 public final class ProjectProperties {
     /** The property name for the project target */
     public final static String PROPERTY_TARGET = "target";
-    public final static String PROPERTY_CONFIGS = "configs";
+    public final static String PROPERTY_APK_CONFIGS = "apk-configurations";
     public final static String PROPERTY_SDK = "sdk-location";
     
     public static enum PropertyType {
@@ -98,7 +98,19 @@
 //               1-------10--------20--------30--------40--------50--------60--------70--------80        
         COMMENT_MAP.put(PROPERTY_TARGET,
                 "# Project target.\n");
-        COMMENT_MAP.put(PROPERTY_SDK, "# location of the SDK. This is only used by Ant\n" +
+        COMMENT_MAP.put(PROPERTY_APK_CONFIGS,
+                "# apk configurations. This property allows creation of APK files with limited\n" +
+                "# resources. For example, if your application contains many locales and\n" +
+                "# you wish to release multiple smaller apks instead of a large one, you can\n" +
+                "# define configuration to create apks with limited language sets.\n" +
+                "# Format is a comma separated list of configuration names. For each\n" +
+                "# configuration, a property will declare the resource configurations to\n" +
+                "# include. Example:\n" +
+                "#     " + PROPERTY_APK_CONFIGS +"=european,northamerica\n" +
+                "#     " + ApkConfigurationHelper.CONFIG_PREFIX + "european=en,fr,it,de,es\n" +
+                "#     " + ApkConfigurationHelper.CONFIG_PREFIX + "northamerica=en,es\n");
+        COMMENT_MAP.put(PROPERTY_SDK,
+                "# location of the SDK. This is only used by Ant\n" +
                 "# For customization when using a Version Control System, please read the\n" +
                 "# header note.\n");
     }
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/ApkConfigEditDialog.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/ApkConfigEditDialog.java
new file mode 100644
index 0000000..1460fd7
--- /dev/null
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/ApkConfigEditDialog.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2009 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.sdkuilib;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.VerifyEvent;
+import org.eclipse.swt.events.VerifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Edit dialog to create/edit APK configuration. The dialog displays 2 text fields for the config
+ * name and its filter.
+ */
+class ApkConfigEditDialog extends Dialog implements ModifyListener, VerifyListener {
+
+    private String mName;
+    private String mFilter;
+    private Text mNameField;
+    private Text mFilterField;
+    private Button mOkButton;
+    
+    /**
+     * Creates an edit dialog with optional initial values for the name and filter.
+     * @param name optional value for the name. Can be null.
+     * @param filter optional value for the filter. Can be null.
+     * @param parentShell the parent shell.
+     */
+    protected ApkConfigEditDialog(String name, String filter, Shell parentShell) {
+        super(parentShell);
+        mName = name;
+        mFilter = filter;
+    }
+    
+    /**
+     * Returns the name of the config. This is only valid if the user clicked OK and {@link #open()}
+     * returned {@link Window#OK}
+     */
+    public String getName() {
+        return mName;
+    }
+    
+    /**
+     * Returns the filter for the config. This is only valid if the user clicked OK and
+     * {@link #open()} returned {@link Window#OK}
+     */
+    public String getFilter() {
+        return mFilter;
+    }
+    
+    @Override
+    protected Control createContents(Composite parent) {
+        Control control = super.createContents(parent);
+
+        mOkButton = getButton(IDialogConstants.OK_ID);
+        validateButtons();
+
+        return control;
+    }
+    
+    @Override
+    protected Control createDialogArea(Composite parent) {
+        Composite composite = new Composite(parent, SWT.NONE);
+        GridLayout layout;
+        composite.setLayout(layout = new GridLayout(2, false));
+        layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
+        layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
+        layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
+        layout.horizontalSpacing = convertHorizontalDLUsToPixels(
+                IDialogConstants.HORIZONTAL_SPACING);
+
+        composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+        
+        Label l = new Label(composite, SWT.NONE);
+        l.setText("Name");
+        
+        mNameField = new Text(composite, SWT.BORDER);
+        mNameField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        mNameField.addVerifyListener(this);
+        if (mName != null) {
+            mNameField.setText(mName);
+        }
+        mNameField.addModifyListener(this);
+
+        l = new Label(composite, SWT.NONE);
+        l.setText("Filter");
+        
+        mFilterField = new Text(composite, SWT.BORDER);
+        mFilterField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        if (mFilter != null) {
+            mFilterField.setText(mFilter);
+        }
+        mFilterField.addVerifyListener(this);
+        mFilterField.addModifyListener(this);
+        
+        applyDialogFont(composite);
+        return composite;
+    }
+    
+    /**
+     * Validates the OK button based on the content of the 2 text fields.
+     */
+    private void validateButtons() {
+        mOkButton.setEnabled(mNameField.getText().trim().length() > 0 &&
+                mFilterField.getText().trim().length() > 0);
+    }
+
+    @Override
+    protected void okPressed() {
+        mName = mNameField.getText();
+        mFilter = mFilterField.getText().trim();
+        super.okPressed();
+    }
+
+    /**
+     * Callback for text modification in the 2 text fields.
+     */
+    public void modifyText(ModifyEvent e) {
+        validateButtons();
+    }
+
+    /**
+     * Callback to ensure the content of the text field are proper.
+     */
+    public void verifyText(VerifyEvent e) {
+        Text source = ((Text)e.getSource());
+        if (source == mNameField) {
+            // check for a-zA-Z0-9.
+            final String text = e.text;
+            final int len = text.length();
+            for (int i = 0 ; i < len; i++) {
+                char letter = text.charAt(i);
+                if (letter > 255 || Character.isLetterOrDigit(letter) == false) {
+                    e.doit = false;
+                    return;
+                }
+            }
+        } else if (source == mFilterField) {
+            // we can't validate the content as its typed, but we can at least ensure the characters
+            // are valid. Same as mNameFiled + the comma.
+            final String text = e.text;
+            final int len = text.length();
+            for (int i = 0 ; i < len; i++) {
+                char letter = text.charAt(i);
+                if (letter > 255 || (Character.isLetterOrDigit(letter) == false && letter != ',')) {
+                    e.doit = false;
+                    return;
+                }
+            }
+        }
+    }
+}
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/ApkConfigWidget.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/ApkConfigWidget.java
new file mode 100644
index 0000000..6bf1df3
--- /dev/null
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/ApkConfigWidget.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2009 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.sdkuilib;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The APK Configuration widget is a table that is added to the given parent composite.
+ * <p/>
+ * To use, create it using {@link #ApkConfigWidget(Composite)} then
+ * call {@link #fillTable(Map) to set the initial list of configurations.
+ */
+public class ApkConfigWidget {
+    private final static int INDEX_NAME = 0;
+    private final static int INDEX_FILTER = 1;
+    
+    private Table mApkConfigTable;
+    private Button mEditButton;
+    private Button mDelButton;
+
+    public ApkConfigWidget(final Composite parent) {
+        final Composite apkConfigComp = new Composite(parent, SWT.NONE);
+        apkConfigComp.setLayoutData(new GridData(GridData.FILL_BOTH));
+        apkConfigComp.setLayout(new GridLayout(2, false));
+        
+        mApkConfigTable = new Table(apkConfigComp, SWT.FULL_SELECTION | SWT.SINGLE | SWT.BORDER);
+        mApkConfigTable.setHeaderVisible(true);
+        mApkConfigTable.setLinesVisible(true);
+
+        GridData data = new GridData();
+        data.grabExcessVerticalSpace = true;
+        data.grabExcessHorizontalSpace = true;
+        data.horizontalAlignment = GridData.FILL;
+        data.verticalAlignment = GridData.FILL;
+        mApkConfigTable.setLayoutData(data);
+
+        // create the table columns
+        final TableColumn column0 = new TableColumn(mApkConfigTable, SWT.NONE);
+        column0.setText("Name");
+        column0.setWidth(100);
+        final TableColumn column1 = new TableColumn(mApkConfigTable, SWT.NONE);
+        column1.setText("Configuration");
+        column1.setWidth(100);
+
+        Composite buttonComp = new Composite(apkConfigComp, SWT.NONE);
+        buttonComp.setLayoutData(new GridData(GridData.FILL_VERTICAL));
+        GridLayout gl;
+        buttonComp.setLayout(gl = new GridLayout(1, false));
+        gl.marginHeight = gl.marginWidth = 0;
+
+        Button newButton = new Button(buttonComp, SWT.PUSH | SWT.FLAT);
+        newButton.setText("New...");
+        newButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+        mEditButton = new Button(buttonComp, SWT.PUSH | SWT.FLAT);
+        mEditButton.setText("Edit...");
+        mEditButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+        mDelButton = new Button(buttonComp, SWT.PUSH | SWT.FLAT);
+        mDelButton.setText("Delete");
+        mDelButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+        
+        newButton.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                ApkConfigEditDialog dlg = new ApkConfigEditDialog(null /*name*/, null /*filter*/,
+                        apkConfigComp.getShell());
+                if (dlg.open() == Dialog.OK) {
+                    TableItem item = new TableItem(mApkConfigTable, SWT.NONE);
+                    item.setText(INDEX_NAME, dlg.getName());
+                    item.setText(INDEX_FILTER, dlg.getFilter());
+                    
+                    onSelectionChanged();
+                }
+            }
+        });
+        
+        mEditButton.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                // get the current selection (single mode so we don't care about any item beyond
+                // index 0).
+                TableItem[] items = mApkConfigTable.getSelection();
+                if (items.length != 0) {
+                    ApkConfigEditDialog dlg = new ApkConfigEditDialog(
+                            items[0].getText(INDEX_NAME), items[0].getText(INDEX_FILTER),
+                            apkConfigComp.getShell());
+                    if (dlg.open() == Dialog.OK) {
+                        items[0].setText(INDEX_NAME, dlg.getName());
+                        items[0].setText(INDEX_FILTER, dlg.getFilter());
+                    }
+                }
+            }
+        });
+        
+        mDelButton.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                // get the current selection (single mode so we don't care about any item beyond
+                // index 0).
+                int[] indices = mApkConfigTable.getSelectionIndices();
+                if (indices.length != 0) {
+                    TableItem item = mApkConfigTable.getItem(indices[0]);
+                    if (MessageDialog.openQuestion(parent.getShell(),
+                            "Apk Configuration deletion",
+                            String.format(
+                                    "Are you sure you want to delete configuration '%1$s'?",
+                                    item.getText(INDEX_NAME)))) {
+                        // delete the item.
+                        mApkConfigTable.remove(indices[0]);
+                        
+                        onSelectionChanged();
+                    }
+                }
+            }
+        });
+        
+        // Add a listener to resize the column to the full width of the table
+        mApkConfigTable.addControlListener(new ControlAdapter() {
+            @Override
+            public void controlResized(ControlEvent e) {
+                Rectangle r = mApkConfigTable.getClientArea();
+                column0.setWidth(r.width * 30 / 100); // 30%  
+                column1.setWidth(r.width * 70 / 100); // 70%
+            }
+        });
+        
+        // add a selection listener on the table, to enable/disable buttons.
+        mApkConfigTable.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                onSelectionChanged();
+            }
+        });
+    }
+    
+    public void fillTable(Map<String, String> apkConfigMap) {
+        // get the names in a list so that we can sort them.
+        if (apkConfigMap != null) {
+            Set<String> keys = apkConfigMap.keySet();
+            String[] keyArray = keys.toArray(new String[keys.size()]);
+            Arrays.sort(keyArray);
+            
+            for (String key : keyArray) {
+                TableItem item = new TableItem(mApkConfigTable, SWT.NONE);
+                item.setText(INDEX_NAME, key);
+                item.setText(INDEX_FILTER, apkConfigMap.get(key));
+            }
+        }
+        
+        onSelectionChanged();
+    }
+
+    public Map<String, String> getApkConfigs() {
+        // go through all the items from the table and fill a new map
+        HashMap<String, String> map = new HashMap<String, String>();
+        
+        TableItem[] items = mApkConfigTable.getItems();
+        for (TableItem item : items) {
+            map.put(item.getText(INDEX_NAME), item.getText(INDEX_FILTER));
+        }
+
+        return map;
+    }
+    
+    /**
+     * Handles table selection changes.
+     */
+    private void onSelectionChanged() {
+        if (mApkConfigTable.getSelectionCount() > 0) {
+            mEditButton.setEnabled(true);
+            mDelButton.setEnabled(true);
+        } else {
+            mEditButton.setEnabled(false);
+            mDelButton.setEnabled(false);
+        }
+    }
+}
diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java
index fc951f2..5f9e9c2 100644
--- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java
+++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/SdkTargetSelector.java
@@ -48,32 +48,57 @@
  */
 public class SdkTargetSelector {
     
-    private final IAndroidTarget[] mTargets;
+    private IAndroidTarget[] mTargets;
+    private final boolean mAllowSelection;
     private final boolean mAllowMultipleSelection;
     private SelectionListener mSelectionListener;
     private Table mTable;
     private Label mDescription;
+    private Composite mInnerGroup;
+    
+    /**
+     * Creates a new SDK Target Selector.
+     * 
+     * @param parent The parent composite where the selector will be added.
+     * @param targets The list of targets. This is <em>not</em> copied, the caller must not modify.
+     *                Targets can be null or an empty array, in which case the table is disabled.
+     * @param allowMultipleSelection True if more than one SDK target can be selected at the same
+     *        time.
+     */
+    public SdkTargetSelector(Composite parent, IAndroidTarget[] targets,
+            boolean allowMultipleSelection) {
+        this(parent, targets, true /*allowSelection*/, allowMultipleSelection);
+    }
 
     /**
      * Creates a new SDK Target Selector.
      * 
      * @param parent The parent composite where the selector will be added.
      * @param targets The list of targets. This is <em>not</em> copied, the caller must not modify.
+     *                Targets can be null or an empty array, in which case the table is disabled.
+     * @param allowSelection True if selection is enabled.
      * @param allowMultipleSelection True if more than one SDK target can be selected at the same
-     *        time.
+     *        time. Used only if allowSelection is true.
      */
     public SdkTargetSelector(Composite parent, IAndroidTarget[] targets,
+            boolean allowSelection,
             boolean allowMultipleSelection) {
-        mTargets = targets;
-
         // Layout has 1 column
-        Composite group = new Composite(parent, SWT.NONE);
-        group.setLayout(new GridLayout());
-        group.setLayoutData(new GridData(GridData.FILL_BOTH));
-        group.setFont(parent.getFont());
+        mInnerGroup = new Composite(parent, SWT.NONE);
+        mInnerGroup.setLayout(new GridLayout());
+        mInnerGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
+        mInnerGroup.setFont(parent.getFont());
         
+        mAllowSelection = allowSelection;
         mAllowMultipleSelection = allowMultipleSelection;
-        mTable = new Table(group, SWT.CHECK | SWT.FULL_SELECTION | SWT.SINGLE | SWT.BORDER);
+        int style = SWT.BORDER;
+        if (allowSelection) {
+            style |= SWT.CHECK | SWT.FULL_SELECTION;
+        }
+        if (!mAllowMultipleSelection) {
+            style |= SWT.SINGLE;
+        }
+        mTable = new Table(mInnerGroup, style);
         mTable.setHeaderVisible(true);
         mTable.setLinesVisible(false);
 
@@ -84,7 +109,7 @@
         data.verticalAlignment = GridData.FILL;
         mTable.setLayoutData(data);
 
-        mDescription = new Label(group, SWT.WRAP);
+        mDescription = new Label(mInnerGroup, SWT.WRAP);
         mDescription.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
 
         // create the table columns
@@ -93,15 +118,26 @@
         final TableColumn column1 = new TableColumn(mTable, SWT.NONE);
         column1.setText("Vendor");
         final TableColumn column2 = new TableColumn(mTable, SWT.NONE);
-        column2.setText("API Level");
+        column2.setText("Version");
         final TableColumn column3 = new TableColumn(mTable, SWT.NONE);
-        column3.setText("SDK");
+        column3.setText("API Level");
 
         adjustColumnsWidth(mTable, column0, column1, column2, column3);
         setupSelectionListener(mTable);
-        fillTable(mTable);
+        setTargets(targets);
         setupTooltip(mTable);
     }
+    
+    /**
+     * Returns the layout data of the inner composite widget that contains the target selector.
+     * By default the layout data is set to a {@link GridData} with a {@link GridData#FILL_BOTH}
+     * mode.
+     * <p/>
+     * This can be useful if you want to change the {@link GridData#horizontalSpan} for example.
+     */
+    public Object getLayoutData() {
+        return mInnerGroup.getLayoutData();
+    }
 
     /**
      * Returns the list of known targets.
@@ -113,6 +149,16 @@
     }
 
     /**
+     * Changes the targets of the SDK Target Selector.
+     * 
+     * @param targets The list of targets. This is <em>not</em> copied, the caller must not modify.
+     */
+    public void setTargets(IAndroidTarget[] targets) {
+        mTargets = targets;
+        fillTable(mTable);
+    }
+
+    /**
      * Sets a selection listener. Set it to null to remove it.
      * The listener will be called <em>after</em> this table processed its selection
      * events so that the caller can see the updated state.
@@ -139,6 +185,10 @@
      * @return true if the target could be selected, false otherwise.
      */
     public boolean setSelection(IAndroidTarget target) {
+        if (!mAllowSelection) {
+            return false;
+        }
+
         boolean found = false;
         boolean modified = false;
         for (TableItem i : mTable.getItems()) {
@@ -224,6 +274,10 @@
      * double-clicked (aka "the default selection").
      */
     private void setupSelectionListener(final Table table) {
+        if (!mAllowSelection) {
+            return;
+        }
+
         // Add a selection listener that will check/uncheck items when they are double-clicked
         table.addSelectionListener(new SelectionListener() {
             /** Default selection means double-click on "most" platforms */
@@ -281,6 +335,9 @@
      * </ul>
      */
     private void fillTable(final Table table) {
+        
+        table.removeAll();
+        
         if (mTargets != null && mTargets.length > 0) {
             table.setEnabled(true);
             for (IAndroidTarget target : mTargets) {
diff --git a/tools/traceview/etc/traceview.bat b/tools/traceview/etc/traceview.bat
index d074f42..a9b573d 100755
--- a/tools/traceview/etc/traceview.bat
+++ b/tools/traceview/etc/traceview.bat
@@ -20,9 +20,9 @@
 rem and set up progdir to be the fully-qualified pathname of its directory.
 set prog=%~f0
 
-rem Change current directory to where traceview is, to avoid issues with directories
-rem containing whitespaces.
-cd %~dp0
+rem Change current directory and drive to where traceview.bat is, to avoid
+rem issues with directories containing whitespaces.
+cd /d %~dp0
 
 set jarfile=traceview.jar
 set frameworkdir=