diff options
| author | 2017-01-31 23:17:25 +0000 | |
|---|---|---|
| committer | 2017-01-31 23:17:29 +0000 | |
| commit | 081d576c77abe5fe3fa3cf5ddfffa8d8baf5474c (patch) | |
| tree | 8821c342dbae04c59f955eedcc36eb6365e7bd8c | |
| parent | 2591ce4d9fb834568b0a5fe3e7d1d8f9368eb56b (diff) | |
| parent | a560cd947be2a7a704d0973a1573d539ff349a07 (diff) | |
Merge "Adding missing filters to suggested actions in Settings that that they match the Optional Steps fliters in setup wizard. Also added some more logging."
| -rw-r--r-- | packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java | 141 | ||||
| -rw-r--r-- | packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java | 40 |
2 files changed, 159 insertions, 22 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java index 21786c917763..13bbc3382ebf 100644 --- a/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java +++ b/packages/SettingsLib/src/com/android/settingslib/SuggestionParser.java @@ -15,21 +15,28 @@ */ package com.android.settingslib; +import android.Manifest; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.annotation.RequiresPermission; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.content.res.Resources; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; import android.util.Xml; -import android.provider.Settings; -import android.accounts.Account; -import android.accounts.AccountManager; -import android.content.pm.PackageManager; -import android.content.res.Resources; import android.view.InflateException; import com.android.settingslib.drawer.Tile; import com.android.settingslib.drawer.TileUtils; @@ -53,6 +60,20 @@ public class SuggestionParser { // If defined and not true, do not should optional step. private static final String META_DATA_IS_SUPPORTED = "com.android.settings.is_supported"; + // If defined, only display this optional step if the current user is of that type. + private static final String META_DATA_REQUIRE_USER_TYPE = + "com.android.settings.require_user_type"; + + // If defined, only display this optional step if a connection is available. + private static final String META_DATA_IS_CONNECTION_REQUIRED = + "com.android.settings.require_connection"; + + // The valid values that setup wizard recognizes for differentiating user types. + private static final String META_DATA_PRIMARY_USER_TYPE_VALUE = "primary"; + private static final String META_DATA_ADMIN_USER_TYPE_VALUE = "admin"; + private static final String META_DATA_GUEST_USER_TYPE_VALUE = "guest"; + private static final String META_DATA_RESTRICTED_USER_TYPE_VALUE = "restricted"; + /** * Allows suggestions to appear after a certain number of days, and to re-appear if dismissed. * For instance: @@ -75,7 +96,7 @@ public class SuggestionParser { private final Context mContext; private final List<SuggestionCategory> mSuggestionList; - private final ArrayMap<Pair<String, String>, Tile> addCache = new ArrayMap<>(); + private final ArrayMap<Pair<String, String>, Tile> mAddCache = new ArrayMap<>(); private final SharedPreferences mSharedPrefs; public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml) { @@ -85,6 +106,14 @@ public class SuggestionParser { mSharedPrefs = sharedPrefs; } + @VisibleForTesting + public SuggestionParser(Context context, SharedPreferences sharedPrefs) { + mContext = context; + mSuggestionList = new ArrayList<SuggestionCategory>(); + mSharedPrefs = sharedPrefs; + Log.wtf(TAG, "Only use this constructor for testing"); + } + public List<Tile> getSuggestions() { List<Tile> suggestions = new ArrayList<>(); final int N = mSuggestionList.size(); @@ -111,23 +140,30 @@ public class SuggestionParser { return false; } - private void readSuggestions(SuggestionCategory category, List<Tile> suggestions) { - int countBefore = suggestions.size(); - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.addCategory(category.category); - if (category.pkg != null) { - intent.setPackage(category.pkg); - } - TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent, - addCache, null, suggestions, true, false); + @VisibleForTesting + public void filterSuggestions(List<Tile> suggestions, int countBefore) { for (int i = countBefore; i < suggestions.size(); i++) { if (!isAvailable(suggestions.get(i)) || !isSupported(suggestions.get(i)) || + !satisifesRequiredUserType(suggestions.get(i)) || !satisfiesRequiredAccount(suggestions.get(i)) || + !satisfiesConnectivity(suggestions.get(i)) || isDismissed(suggestions.get(i))) { suggestions.remove(i--); } } + } + + private void readSuggestions(SuggestionCategory category, List<Tile> suggestions) { + int countBefore = suggestions.size(); + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(category.category); + if (category.pkg != null) { + intent.setPackage(category.pkg); + } + TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent, + mAddCache, null, suggestions, true, false); + filterSuggestions(suggestions, countBefore); if (!category.multiple && suggestions.size() > (countBefore + 1)) { // If there are too many, remove them all and only re-add the one with the highest // priority. @@ -146,32 +182,77 @@ public class SuggestionParser { } private boolean isAvailable(Tile suggestion) { - String featureRequired = suggestion.metaData.getString(META_DATA_REQUIRE_FEATURE); - if (featureRequired != null) { - return mContext.getPackageManager().hasSystemFeature(featureRequired); + final String featuresRequired = suggestion.metaData.getString(META_DATA_REQUIRE_FEATURE); + if (featuresRequired != null) { + for (String feature : featuresRequired.split(",")) { + if (TextUtils.isEmpty(feature)) { + Log.w(TAG, "Found empty substring when parsing required features: " + + featuresRequired); + } else if (!mContext.getPackageManager().hasSystemFeature(feature)) { + Log.i(TAG, suggestion.title + " requires unavailable feature " + feature); + return false; + } + } + } + return true; + } + + @RequiresPermission(Manifest.permission.MANAGE_USERS) + private boolean satisifesRequiredUserType(Tile suggestion) { + final String requiredUser = suggestion.metaData.getString(META_DATA_REQUIRE_USER_TYPE); + if (requiredUser != null) { + final UserManager userManager = mContext.getSystemService(UserManager.class); + UserInfo userInfo = userManager.getUserInfo(UserHandle.myUserId()); + for (String userType : requiredUser.split("\\|")) { + final boolean primaryUserCondtionMet = userInfo.isPrimary() + && META_DATA_PRIMARY_USER_TYPE_VALUE.equals(userType); + final boolean adminUserConditionMet = userInfo.isAdmin() + && META_DATA_ADMIN_USER_TYPE_VALUE.equals(userType); + final boolean guestUserCondtionMet = userInfo.isGuest() + && META_DATA_GUEST_USER_TYPE_VALUE.equals(userType); + final boolean restrictedUserCondtionMet = userInfo.isRestricted() + && META_DATA_RESTRICTED_USER_TYPE_VALUE.equals(userType); + if (primaryUserCondtionMet || adminUserConditionMet || guestUserCondtionMet + || restrictedUserCondtionMet) { + return true; + } + } + Log.i(TAG, suggestion.title + " requires user type " + requiredUser); + return false; } return true; } public boolean satisfiesRequiredAccount(Tile suggestion) { - String requiredAccountType = suggestion.metaData.getString(META_DATA_REQUIRE_ACCOUNT); + final String requiredAccountType = suggestion.metaData.getString(META_DATA_REQUIRE_ACCOUNT); if (requiredAccountType == null) { return true; } AccountManager accountManager = AccountManager.get(mContext); Account[] accounts = accountManager.getAccountsByType(requiredAccountType); - return accounts.length > 0; + boolean satisfiesRequiredAccount = accounts.length > 0; + if (!satisfiesRequiredAccount) { + Log.i(TAG, suggestion.title + " requires unavailable account type " + + requiredAccountType); + } + return satisfiesRequiredAccount; } public boolean isSupported(Tile suggestion) { - int isSupportedResource = suggestion.metaData.getInt(META_DATA_IS_SUPPORTED); + final int isSupportedResource = suggestion.metaData.getInt(META_DATA_IS_SUPPORTED); try { if (suggestion.intent == null) { return false; } final Resources res = mContext.getPackageManager().getResourcesForActivity( suggestion.intent.getComponent()); - return isSupportedResource != 0 ? res.getBoolean(isSupportedResource) : true; + boolean isSupported = + isSupportedResource != 0 ? res.getBoolean(isSupportedResource) : true; + if (!isSupported) { + Log.i(TAG, suggestion.title + " requires unsupported resource " + + isSupportedResource); + } + return isSupported; } catch (PackageManager.NameNotFoundException e) { Log.w(TAG, "Cannot find resources for " + suggestion.intent.getComponent()); return false; @@ -181,6 +262,22 @@ public class SuggestionParser { } } + private boolean satisfiesConnectivity(Tile suggestion) { + final boolean isConnectionRequired = + suggestion.metaData.getBoolean(META_DATA_IS_CONNECTION_REQUIRED); + if (!isConnectionRequired) { + return true; + } + ConnectivityManager cm = + (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo netInfo = cm.getActiveNetworkInfo(); + boolean satisfiesConnectivity = netInfo != null && netInfo.isConnectedOrConnecting(); + if (!satisfiesConnectivity) { + Log.i(TAG, suggestion.title + " is missing required connection."); + } + return satisfiesConnectivity; + } + public boolean isCategoryDone(String category) { String name = Settings.Secure.COMPLETED_CATEGORY_PREFIX + category; return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) != 0; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java index c95cac5c8c67..021a96cedd6d 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java @@ -34,9 +34,11 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings.Global; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.Pair; +import com.android.settingslib.SuggestionParser; import com.android.settingslib.TestConfig; import static org.mockito.Mockito.atLeastOnce; @@ -156,6 +158,32 @@ public class TileUtilsTest { } @Test + public void getTilesForIntent_shouldSkipFilteredApps() { + final String testCategory = "category1"; + Intent intent = new Intent(); + Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>(); + List<Tile> outTiles = new ArrayList<>(); + List<ResolveInfo> info = new ArrayList<>(); + ResolveInfo resolveInfo = newInfo(true, null /* category */, null, URI_GET_ICON, + URI_GET_SUMMARY); + addMetadataToInfo(resolveInfo, "com.android.settings.require_account", "com.google"); + addMetadataToInfo(resolveInfo, "com.android.settings.require_connection", "true"); + info.add(resolveInfo); + + when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt())) + .thenReturn(info); + + TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache, + null /* defaultCategory */, outTiles, false /* usePriority */, + false /* checkCategory */); + + assertThat(outTiles.size()).isEqualTo(1); + SuggestionParser parser = new SuggestionParser(mContext, null); + parser.filterSuggestions(outTiles, 0); + assertThat(outTiles.size()).isEqualTo(0); + } + + @Test public void getCategories_shouldHandleExtraIntentAction() { final String testCategory = "category1"; final String testAction = "action1"; @@ -309,4 +337,16 @@ public class TileUtilsTest { } return info; } + + private void addMetadataToInfo(ResolveInfo info, String key, String value) { + if (!TextUtils.isEmpty(key)) { + if (info.activityInfo == null) { + info.activityInfo = new ActivityInfo(); + } + if (info.activityInfo.metaData == null) { + info.activityInfo.metaData = new Bundle(); + } + info.activityInfo.metaData.putString(key, value); + } + } } |