summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ido Ofir <iofir@google.com> 2017-01-31 23:17:25 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2017-01-31 23:17:29 +0000
commit081d576c77abe5fe3fa3cf5ddfffa8d8baf5474c (patch)
tree8821c342dbae04c59f955eedcc36eb6365e7bd8c
parent2591ce4d9fb834568b0a5fe3e7d1d8f9368eb56b (diff)
parenta560cd947be2a7a704d0973a1573d539ff349a07 (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.java141
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java40
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);
+ }
+ }
}