From 82163dec1806a63084a436bb28683fc668219d02 Mon Sep 17 00:00:00 2001 From: Christian Mehlmauer Date: Tue, 25 May 2010 18:39:46 +0200 Subject: [PATCH 1/2] replaced deprecated getIntent with parseURI Change-Id: Iabf82ff0f9be2a76dece9aafe8603bf83ac7d049 --- src/com/android/settings/quicklaunch/QuickLaunchSettings.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/quicklaunch/QuickLaunchSettings.java b/src/com/android/settings/quicklaunch/QuickLaunchSettings.java index ca5d3ab7dc7..2d2b01cd72f 100644 --- a/src/com/android/settings/quicklaunch/QuickLaunchSettings.java +++ b/src/com/android/settings/quicklaunch/QuickLaunchSettings.java @@ -204,7 +204,7 @@ public class QuickLaunchSettings extends PreferenceActivity implements 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 @@ public class QuickLaunchSettings extends PreferenceActivity implements 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); From b2dd90383bae9f3ca0a99b59d3f5992e7fd5ad48 Mon Sep 17 00:00:00 2001 From: Anders Hammar1 Date: Thu, 8 Apr 2010 10:03:50 +0200 Subject: [PATCH 2/2] Settings: Add a hook for operator or vendor specific settings. The Settings application now provides a hook that can be used by an operator or a vendor specific application to add an activity of choice in the settings menu. Change-Id: Id55da9fd4262bbfc6a5abf863799c747b0d75b24 --- res/xml/settings.xml | 14 ++ .../settings/IconPreferenceScreen.java | 23 +++ src/com/android/settings/Settings.java | 8 + src/com/android/settings/Utils.java | 113 ++++++++++++++- tests/AndroidManifest.xml | 21 +++ .../res/drawable/ic_settings_applications.png | Bin 0 -> 1723 bytes tests/res/layout/manufacturer_main.xml | 22 +++ tests/res/layout/operator_main.xml | 22 +++ tests/res/values/strings.xml | 6 + .../android/settings/SettingsHookTests.java | 137 ++++++++++++++++++ .../android/settings/tests/Manufacturer.java | 29 ++++ .../com/android/settings/tests/Operator.java | 30 ++++ 12 files changed, 424 insertions(+), 1 deletion(-) create mode 100755 tests/res/drawable/ic_settings_applications.png create mode 100644 tests/res/layout/manufacturer_main.xml create mode 100644 tests/res/layout/operator_main.xml create mode 100644 tests/src/com/android/settings/SettingsHookTests.java create mode 100644 tests/src/com/android/settings/tests/Manufacturer.java create mode 100644 tests/src/com/android/settings/tests/Operator.java diff --git a/res/xml/settings.xml b/res/xml/settings.xml index 22f252345bd..add38da5d31 100644 --- a/res/xml/settings.xml +++ b/res/xml/settings.xml @@ -20,6 +20,20 @@ android:title="@string/settings_label" android:key="parent"> + + + + + + + + + + + + diff --git a/src/com/android/settings/IconPreferenceScreen.java b/src/com/android/settings/IconPreferenceScreen.java index c7c53038ed9..31abf0a8694 100644 --- a/src/com/android/settings/IconPreferenceScreen.java +++ b/src/com/android/settings/IconPreferenceScreen.java @@ -22,6 +22,7 @@ import android.graphics.drawable.Drawable; 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 @@ public class IconPreferenceScreen extends Preference { 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 5309cf57a26..c53c2fa5520 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -30,6 +30,9 @@ public class Settings extends PreferenceActivity { 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 @@ public class Settings extends PreferenceActivity { 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 d4f1f1155c4..b29ec06f588 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -22,8 +22,14 @@ import android.content.pm.ApplicationInfo; 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; @@ -34,6 +40,24 @@ public class Utils { */ 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. @@ -89,11 +113,98 @@ public class Utils { return true; } + /** + * 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 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/tests/AndroidManifest.xml b/tests/AndroidManifest.xml index 8a0ce218230..fe4dd95d6db 100644 --- a/tests/AndroidManifest.xml +++ b/tests/AndroidManifest.xml @@ -29,8 +29,29 @@ + + + + + + + + + + + + + + + + + + + diff --git a/tests/res/drawable/ic_settings_applications.png b/tests/res/drawable/ic_settings_applications.png new file mode 100755 index 0000000000000000000000000000000000000000..5cea33f487a41ed762dfdb099a479faee02943b8 GIT binary patch literal 1723 zcmV;s21NOZP)Now?>lX(0y==iYPAJ?DL&_xs*}wY4?jO>PR|4R3x1 z{0#URc*9$ulttCm)ddR+3yHf)SXfxta9LT|pPtqQc!~oJ4Gq6ot=98<_UsXYf`WF1 z_?49vVQy|t2n`Lrsnu$~^qPSO4;~Z-1O)sM6B8qlf!%EL^Yg;<=g)=J)z$a&^78I_ zr9%7m?aK%b4i>`0!*^Y`BO)Rksf6vO8<3iyR4N_UK+w^>X#@{v ztGxC?BH}1_pX10V!wi2m@RX!e^jL zYMafru1>E#1Jr$mLg5&Jx&?$s(ik~Mso3rI_5NBhKmv&}RR!0>Slo_;_*?=&QDZO|{?_aDH$X%S4vZ)9B#dg~y4LARn=zk?@h&CP*ViX> zbabenK7D!v>B%E7s3O{6Zu0Qq!}a31FJ8Q`5ip-hfCu3a0jQC%Ghk*MOAK9IT@Kxa zstEh{@BfdDu#@nyu`yv}WJHCG_F`IETKTbK$08uXI=puk0Q`>q-oAJ5UTA)PzEzAP zI~K^0=2AbcnESS^tu1tVdipQi_rhI%rBM}e$EKE&J6~T~ePMtcHg!k`b zO$ktc%zZIl!uSZ|*LdxFckiLINTl=0lP68l(a~3$o11T*J$u&dGX}&&Ohna%0Y`S2 zl9FPE!M%k;Y*=HbaguP8$)v_yWmZ;JabjYk72fJH_Nj$;vXOXq|BrXe;C+hVwSK_u zIocExIgtig6`8vPNC3J?yoZht6HO2;3=R$|QHT^LCnr}PKYm;dkJW%BS3%G?-}}D^ zoSK?y?(grff?S$N>Uff^WM8tR8m z*u@1fW)#&$LNu?1%hkd~Uqc~thsF;8d~XBJV3dT*R^E4achfLyP&E~S0Ta+;P-~(a zB=J)7DA<$@ZuSxwpjV2Z8bPgm&j8f`#(AcqqM`+em8YktUrb6$vO{O9u&xS(TY0!Q zMGV}luD7?h6$bq-C9osO9K{O#EXvHx96EI9kQ(aJgz3v5+T`k8WV8|(bvMvg5(uAr zA&T|Lkt585RM6Y^a7e3EJ(ton8jTT2U?a}dN-0MrOMqFYGkJ4t3K2)pfO!v*Z*aA8 z8ulJ=*2BKZg`5Qn`*rk8q!k~588g)B8iE#Gr>wenv+qKpL}jUvn9mS>sLEaV_7dkK zBbo_Owi}68FTvk{tmg~rc2DbYZFE=-L!yF1H0Rhe7G-P=p>akp+RashE zYDZqb{`n7efTO%bguXF8KHjw5IDi6a!zV?g-X{neJ0xuLngMKJ6zf)Q92Xa-#@x@d zv$N}kIO_v;JUErY`Y|nstLMQtNhyNd$p8-Dueg^yuY}Ni1U;rF(dO|jR RkVXIi002ovPDHLkV1l4CDjWa+ literal 0 HcmV?d00001 diff --git a/tests/res/layout/manufacturer_main.xml b/tests/res/layout/manufacturer_main.xml new file mode 100644 index 00000000000..8f8c48f1f26 --- /dev/null +++ b/tests/res/layout/manufacturer_main.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/tests/res/layout/operator_main.xml b/tests/res/layout/operator_main.xml new file mode 100644 index 00000000000..3cf8e00aab2 --- /dev/null +++ b/tests/res/layout/operator_main.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml index b06782feed9..cd94a2da24f 100644 --- a/tests/res/values/strings.xml +++ b/tests/res/values/strings.xml @@ -20,4 +20,10 @@ Enable Discoverable + Hello Operator! + Operator + Operator hook that can be used to start activity of choice + Hello Manufacturer! + Manufacturer + Manufacturer hook that can be used to start activity of choice diff --git a/tests/src/com/android/settings/SettingsHookTests.java b/tests/src/com/android/settings/SettingsHookTests.java new file mode 100644 index 00000000000..b14e5bcfe25 --- /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 { + + 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 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 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 00000000000..692e6a8c287 --- /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 00000000000..8a34363da2a --- /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); + } + +}