merge from froyo-plus-aosp

Change-Id: I6c6ee9c9567f5aef7e1beb585296c1e2fb884e9a
diff --git a/res/xml/settings.xml b/res/xml/settings.xml
index 0f19442..326c494 100644
--- a/res/xml/settings.xml
+++ b/res/xml/settings.xml
@@ -20,6 +20,20 @@
     android:title="@string/settings_label"
     android:key="parent">
 
+        <!-- Operator hook -->
+
+        <com.android.settings.IconPreferenceScreen
+            android:key="operator_settings">
+            <intent android:action="com.android.settings.OPERATOR_APPLICATION_SETTING" />
+        </com.android.settings.IconPreferenceScreen>
+
+        <!-- Manufacturer hook -->
+
+        <com.android.settings.IconPreferenceScreen
+            android:key="manufacturer_settings">
+            <intent android:action="com.android.settings.MANUFACTURER_APPLICATION_SETTING" />
+        </com.android.settings.IconPreferenceScreen>
+
         <com.android.settings.IconPreferenceScreen
             android:title="@string/radio_controls_title"
             settings:icon="@drawable/ic_settings_wireless">
diff --git a/src/com/android/settings/IconPreferenceScreen.java b/src/com/android/settings/IconPreferenceScreen.java
index c7c5303..31abf0a 100644
--- a/src/com/android/settings/IconPreferenceScreen.java
+++ b/src/com/android/settings/IconPreferenceScreen.java
@@ -22,6 +22,7 @@
 import android.preference.Preference;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.ImageView;
 
 public class IconPreferenceScreen extends Preference {
@@ -48,4 +49,26 @@
             imageView.setImageDrawable(mIcon);
         }
     }
+
+    /**
+     * Sets the icon for this Preference with a Drawable.
+     *
+     * @param icon The icon for this Preference
+     */
+    public void setIcon(Drawable icon) {
+        if ((icon == null && mIcon != null) || (icon != null && !icon.equals(mIcon))) {
+            mIcon = icon;
+            notifyChanged();
+        }
+    }
+
+    /**
+     * Returns the icon of this Preference.
+     *
+     * @return The icon.
+     * @see #setIcon(Drawable)
+     */
+    public Drawable getIcon() {
+        return mIcon;
+    }
 }
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 5309cf5..c53c2fa 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -30,6 +30,9 @@
     private static final String KEY_SEARCH_SETTINGS = "search_settings";
     private static final String KEY_DOCK_SETTINGS = "dock_settings";
     
+    private static final String KEY_OPERATOR_SETTINGS = "operator_settings";
+    private static final String KEY_MANUFACTURER_SETTINGS = "manufacturer_settings";
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -46,6 +49,11 @@
         if (getResources().getBoolean(R.bool.has_dock_settings) == false && dockSettings != null) {
             parent.removePreference(dockSettings);
         }
+
+        Utils.updatePreferenceToSpecificActivityFromMetaDataOrRemove(this, parent,
+                KEY_OPERATOR_SETTINGS);
+        Utils.updatePreferenceToSpecificActivityFromMetaDataOrRemove(this, parent,
+                KEY_MANUFACTURER_SETTINGS);
     }
     
     @Override
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index d4f1f11..b29ec06f 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -22,8 +22,14 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.SystemProperties;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.preference.Preference;
 import android.preference.PreferenceGroup;
+import android.text.TextUtils;
 
 import java.util.List;
 
@@ -35,6 +41,24 @@
     public static final int UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY = 1;
 
     /**
+     * Name of the meta-data item that should be set in the AndroidManifest.xml
+     * to specify the icon that should be displayed for the preference.
+     */
+    private static final String META_DATA_PREFERENCE_ICON = "com.android.settings.icon";
+
+    /**
+     * Name of the meta-data item that should be set in the AndroidManifest.xml
+     * to specify the title that should be displayed for the preference.
+     */
+    private static final String META_DATA_PREFERENCE_TITLE = "com.android.settings.title";
+
+    /**
+     * Name of the meta-data item that should be set in the AndroidManifest.xml
+     * to specify the summary text that should be displayed for the preference.
+     */
+    private static final String META_DATA_PREFERENCE_SUMMARY = "com.android.settings.summary";
+
+    /**
      * Finds a matching activity for a preference's intent. If a matching
      * activity is not found, it will remove the preference.
      *
@@ -90,10 +114,97 @@
     }
 
     /**
+     * Finds a matching activity for a preference's intent. If a matching
+     * activity is not found, it will remove the preference. The icon, title and
+     * summary of the preference will also be updated with the values retrieved
+     * from the activity's meta-data elements. If no meta-data elements are
+     * specified then the preference title will be set to match the label of the
+     * activity, an icon and summary text will not be displayed.
+     *
+     * @param context The context.
+     * @param parentPreferenceGroup The preference group that contains the
+     *            preference whose intent is being resolved.
+     * @param preferenceKey The key of the preference whose intent is being
+     *            resolved.
+     *
+     * @return Whether an activity was found. If false, the preference was
+     *         removed.
+     *
+     * @see {@link #META_DATA_PREFERENCE_ICON}
+     *      {@link #META_DATA_PREFERENCE_TITLE}
+     *      {@link #META_DATA_PREFERENCE_SUMMARY}
+     */
+    public static boolean updatePreferenceToSpecificActivityFromMetaDataOrRemove(Context context,
+            PreferenceGroup parentPreferenceGroup, String preferenceKey) {
+
+        IconPreferenceScreen preference = (IconPreferenceScreen)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, PackageManager.GET_META_DATA);
+            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) {
+                    Drawable icon = null;
+                    String title = null;
+                    String summary = null;
+
+                    // Get the activity's meta-data
+                    try {
+                        Resources res = pm
+                                .getResourcesForApplication(resolveInfo.activityInfo.packageName);
+                        Bundle metaData = resolveInfo.activityInfo.metaData;
+
+                        if (res != null && metaData != null) {
+                            icon = res.getDrawable(metaData.getInt(META_DATA_PREFERENCE_ICON));
+                            title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE));
+                            summary = res.getString(metaData.getInt(META_DATA_PREFERENCE_SUMMARY));
+                        }
+                    } catch (NameNotFoundException e) {
+                        // Ignore
+                    } catch (NotFoundException e) {
+                        // Ignore
+                    }
+
+                    // Set the preference title to the activity's label if no
+                    // meta-data is found
+                    if (TextUtils.isEmpty(title)) {
+                        title = resolveInfo.loadLabel(pm).toString();
+                    }
+
+                    // Set icon, title and summary for the preference
+                    preference.setIcon(icon);
+                    preference.setTitle(title);
+                    preference.setSummary(summary);
+
+                    // 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 false;
+    }
+
+    /**
      * Returns true if Monkey is running.
      */
     public static boolean isMonkeyRunning() {
         return SystemProperties.getBoolean("ro.monkey", false);
     }
-
 }
diff --git a/src/com/android/settings/quicklaunch/QuickLaunchSettings.java b/src/com/android/settings/quicklaunch/QuickLaunchSettings.java
index ca5d3ab..2d2b01c 100644
--- a/src/com/android/settings/quicklaunch/QuickLaunchSettings.java
+++ b/src/com/android/settings/quicklaunch/QuickLaunchSettings.java
@@ -204,7 +204,7 @@
         return true;
     }
 
-    public boolean onItemLongClick(AdapterView parent, View view, int position, long id) {
+    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
         
         // Open the clear shortcut dialog
         Preference pref = (Preference) getPreferenceScreen().getRootAdapter().getItem(position);
@@ -314,7 +314,7 @@
             String intentUri = c.getString(intentColumn);
             PackageManager packageManager = getPackageManager();
             try {
-                Intent intent = Intent.getIntent(intentUri);
+                Intent intent = Intent.parseUri(intentUri, 0);
                 ResolveInfo info = packageManager.resolveActivity(intent, 0);
                 if (info != null) {
                     title = info.loadLabel(packageManager);
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 6b9e050..e125128 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -30,8 +30,29 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:name="Operator" android:label="Operator Hook Test" >
+            <intent-filter>
+                <action android:name="com.android.settings.OPERATOR_APPLICATION_SETTING" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.title" android:resource="@string/operator_settings_title" />
+            <meta-data android:name="com.android.settings.summary" android:resource="@string/operator_settings_summary" />
+            <meta-data android:name="com.android.settings.icon" android:resource="@drawable/ic_settings_applications" />
+        </activity>
+        <activity android:name="Manufacturer" android:label="Manufacturer Hook Test" >
+            <intent-filter>
+                <action android:name="com.android.settings.MANUFACTURER_APPLICATION_SETTING" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.title" android:resource="@string/manufacturer_settings_title" />
+            <meta-data android:name="com.android.settings.summary" android:resource="@string/manufacturer_settings_summary" />
+            <meta-data android:name="com.android.settings.icon" android:resource="@drawable/ic_settings_applications" />
+        </activity>
     </application>
 
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="com.android.settings"
+        android:label="Settings App Tests">
+    </instrumentation>
+
     <instrumentation android:name="SettingsLaunchPerformance"
         android:targetPackage="com.android.settings"
         android:label="Settings Launch Performance">
diff --git a/tests/res/drawable/ic_settings_applications.png b/tests/res/drawable/ic_settings_applications.png
new file mode 100755
index 0000000..5cea33f
--- /dev/null
+++ b/tests/res/drawable/ic_settings_applications.png
Binary files differ
diff --git a/tests/res/layout/manufacturer_main.xml b/tests/res/layout/manufacturer_main.xml
new file mode 100644
index 0000000..8f8c48f
--- /dev/null
+++ b/tests/res/layout/manufacturer_main.xml
@@ -0,0 +1,22 @@
+<?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:text="@string/manufacturer_hello" />
+</LinearLayout>
diff --git a/tests/res/layout/operator_main.xml b/tests/res/layout/operator_main.xml
new file mode 100644
index 0000000..3cf8e00
--- /dev/null
+++ b/tests/res/layout/operator_main.xml
@@ -0,0 +1,22 @@
+<?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:text="@string/operator_hello" />
+</LinearLayout>
diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml
index 00de425..9fb98f9 100644
--- a/tests/res/values/strings.xml
+++ b/tests/res/values/strings.xml
@@ -22,4 +22,10 @@
     <string name="discoverable">Discoverable</string>
     <string name="start_scan">Start scan</string>
     <string name="stop_scan">Stop scan</string>
+    <string name="operator_hello">Hello Operator!</string>
+    <string name="operator_settings_title">Operator</string>
+    <string name="operator_settings_summary">Operator hook that can be used to start activity of choice</string>
+    <string name="manufacturer_hello">Hello Manufacturer!</string>
+    <string name="manufacturer_settings_title">Manufacturer</string>
+    <string name="manufacturer_settings_summary">Manufacturer hook that can be used to start activity of choice</string>
 </resources>
diff --git a/tests/src/com/android/settings/SettingsHookTests.java b/tests/src/com/android/settings/SettingsHookTests.java
new file mode 100644
index 0000000..b14e5bc
--- /dev/null
+++ b/tests/src/com/android/settings/SettingsHookTests.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2010 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.settings;
+
+import com.android.settings.tests.Manufacturer;
+import com.android.settings.tests.Operator;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceGroup;
+import android.test.ActivityInstrumentationTestCase2;
+
+import java.util.List;
+
+/**
+ * Tests for the Settings operator/manufacturer hook.
+ *
+ * Running all tests:
+ *
+ *   make SettingsTests
+ *   adb push SettingsTests.apk /system/app/SettingsTests.apk
+ *   adb shell am instrument \
+ *    -w com.android.settings.tests/android.test.InstrumentationTestRunner
+ */
+public class SettingsHookTests extends ActivityInstrumentationTestCase2<Settings> {
+
+    private static final String PACKAGE_NAME = "com.android.settings.tests";
+
+    private static final String KEY_SETTINGS_ROOT = "parent";
+    private static final String KEY_SETTINGS_OPERATOR = "operator_settings";
+    private static final String KEY_SETTINGS_MANUFACTURER = "manufacturer_settings";
+
+    private static final String INTENT_OPERATOR_HOOK = "com.android.settings.OPERATOR_APPLICATION_SETTING";
+    private static final String INTENT_MANUFACTURER_HOOK = "com.android.settings.MANUFACTURER_APPLICATION_SETTING";
+
+    private Settings mSettings;
+
+    public SettingsHookTests() {
+        super("com.android.settings", Settings.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mSettings = getActivity();
+    }
+
+    /**
+     * Test that the operator/manufacturer settings hook test application is
+     * available and that it's installed in the device's system image.
+     */
+    public void testSettingsHookTestAppAvailable() throws Exception {
+        Context context = mSettings.getApplicationContext();
+        PackageManager pm = context.getPackageManager();
+        ApplicationInfo applicationInfo = pm.getApplicationInfo(PACKAGE_NAME, 0);
+        assertTrue((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+    }
+
+    /**
+     * Test that the operator test activity has registered an intent-filter for
+     * an action named 'android.settings.OPERATOR_APPLICATION_SETTING'.
+     */
+    public void testOperatorIntentFilter() {
+        boolean result = false;
+        Context context = mSettings.getApplicationContext();
+        PackageManager pm = context.getPackageManager();
+        Intent intent = new Intent(INTENT_OPERATOR_HOOK);
+        List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
+        for (ResolveInfo resolveInfo : list) {
+            if (resolveInfo.activityInfo.packageName.equals(PACKAGE_NAME)) {
+                result = true;
+            }
+        }
+        assertTrue("Intent-filer not found", result);
+    }
+
+    /**
+     * Test that the manufacturer test activity has registered an intent-filter
+     * for an action named 'android.settings.MANUFACTURER_APPLICATION_SETTING'.
+     */
+    public void testManufacturerIntentFilter() {
+        boolean result = false;
+        Context context = mSettings.getApplicationContext();
+        PackageManager pm = context.getPackageManager();
+        Intent intent = new Intent(INTENT_MANUFACTURER_HOOK);
+        List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
+        for (ResolveInfo resolveInfo : list) {
+            if (resolveInfo.activityInfo.packageName.equals(PACKAGE_NAME)) {
+                result = true;
+            }
+        }
+        assertTrue("Intent-filer not found", result);
+    }
+
+    /**
+     * Test that the operator preference is available in the Settings
+     * application.
+     */
+    public void testOperatorPreferenceAvailable() {
+        PreferenceGroup root = (PreferenceGroup)mSettings.findPreference(KEY_SETTINGS_ROOT);
+        Preference operatorPreference = root.findPreference(KEY_SETTINGS_OPERATOR);
+        assertNotNull(operatorPreference);
+    }
+
+    /**
+     * Test that the manufacturer preference is available in the Settings
+     * application.
+     */
+    public void testManufacturerPreferenceAvailable() {
+        PreferenceGroup root = (PreferenceGroup)mSettings.findPreference(KEY_SETTINGS_ROOT);
+        Preference manufacturerHook = root.findPreference(KEY_SETTINGS_MANUFACTURER);
+        assertNotNull(manufacturerHook);
+    }
+
+}
diff --git a/tests/src/com/android/settings/tests/Manufacturer.java b/tests/src/com/android/settings/tests/Manufacturer.java
new file mode 100644
index 0000000..692e6a8
--- /dev/null
+++ b/tests/src/com/android/settings/tests/Manufacturer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 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.settings.tests;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class Manufacturer extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.manufacturer_main);
+    }
+}
diff --git a/tests/src/com/android/settings/tests/Operator.java b/tests/src/com/android/settings/tests/Operator.java
new file mode 100644
index 0000000..8a34363
--- /dev/null
+++ b/tests/src/com/android/settings/tests/Operator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 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.settings.tests;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class Operator extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.operator_main);
+    }
+
+}