Remove old suggestion code.
Change-Id: I2ab9b32ea7622228b0d61b3a16f0695ab04597d7 Fixes: 110361022 Test: robotests
This commit is contained in:
@@ -34,7 +34,6 @@ import android.provider.Settings.Global;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -96,11 +95,7 @@ public class TileUtils {
|
||||
/**
|
||||
* The key used to get the category from metadata of activities of action
|
||||
* {@link #EXTRA_SETTINGS_ACTION}
|
||||
* The value must be one of:
|
||||
* <li>com.android.settings.category.wireless</li>
|
||||
* <li>com.android.settings.category.device</li>
|
||||
* <li>com.android.settings.category.personal</li>
|
||||
* <li>com.android.settings.category.system</li>
|
||||
* The value must be from {@link CategoryKey}.
|
||||
*/
|
||||
private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
|
||||
|
||||
@@ -171,17 +166,6 @@ public class TileUtils {
|
||||
public static final String META_DATA_PREFERENCE_SUMMARY_URI =
|
||||
"com.android.settings.summary_uri";
|
||||
|
||||
/**
|
||||
* Name of the meta-data item that should be set in the AndroidManifest.xml to specify the
|
||||
* custom view which should be displayed for the preference. The custom view will be inflated
|
||||
* as a remote view.
|
||||
*
|
||||
* This also can be used with {@link #META_DATA_PREFERENCE_SUMMARY_URI}, by setting the id
|
||||
* of the summary TextView to '@android:id/summary'.
|
||||
*/
|
||||
public static final String META_DATA_PREFERENCE_CUSTOM_VIEW =
|
||||
"com.android.settings.custom_view";
|
||||
|
||||
public static final String SETTING_PKG = "com.android.settings";
|
||||
|
||||
/**
|
||||
@@ -442,11 +426,6 @@ public class TileUtils {
|
||||
keyHint = metaData.getString(META_DATA_PREFERENCE_KEYHINT);
|
||||
}
|
||||
}
|
||||
if (metaData.containsKey(META_DATA_PREFERENCE_CUSTOM_VIEW)) {
|
||||
int layoutId = metaData.getInt(META_DATA_PREFERENCE_CUSTOM_VIEW);
|
||||
tile.remoteViews = new RemoteViews(applicationInfo.packageName, layoutId);
|
||||
updateSummaryAndTitle(context, providerMap, tile);
|
||||
}
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
|
||||
if (DEBUG) Log.d(LOG_TAG, "Couldn't find info", e);
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.settingslib.suggestions;
|
||||
|
||||
public class SuggestionCategory {
|
||||
public String category;
|
||||
public String pkg;
|
||||
public boolean multiple;
|
||||
public boolean exclusive;
|
||||
public long exclusiveExpireDaysInMillis;
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.settingslib.suggestions;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.settingslib.drawer.Tile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class SuggestionList {
|
||||
// Category -> list of suggestion map
|
||||
private final Map<SuggestionCategory, List<Tile>> mSuggestions;
|
||||
|
||||
// A flatten list of all suggestions.
|
||||
private List<Tile> mSuggestionList;
|
||||
|
||||
public SuggestionList() {
|
||||
mSuggestions = new ArrayMap<>();
|
||||
}
|
||||
|
||||
public void addSuggestions(SuggestionCategory category, List<Tile> suggestions) {
|
||||
mSuggestions.put(category, suggestions);
|
||||
}
|
||||
|
||||
public List<Tile> getSuggestions() {
|
||||
if (mSuggestionList != null) {
|
||||
return mSuggestionList;
|
||||
}
|
||||
mSuggestionList = new ArrayList<>();
|
||||
for (List<Tile> suggestions : mSuggestions.values()) {
|
||||
mSuggestionList.addAll(suggestions);
|
||||
}
|
||||
dedupeSuggestions(mSuggestionList);
|
||||
return mSuggestionList;
|
||||
}
|
||||
|
||||
public boolean isExclusiveSuggestionCategory() {
|
||||
if (mSuggestions.size() != 1) {
|
||||
// If there is no category, or more than 1 category, it's not exclusive by definition.
|
||||
return false;
|
||||
}
|
||||
for (SuggestionCategory category : mSuggestions.keySet()) {
|
||||
if (category.exclusive) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter suggestions list so they are all unique.
|
||||
*/
|
||||
private void dedupeSuggestions(List<Tile> suggestions) {
|
||||
final Set<String> intents = new ArraySet<>();
|
||||
for (int i = suggestions.size() - 1; i >= 0; i--) {
|
||||
final Tile suggestion = suggestions.get(i);
|
||||
final String intentUri = suggestion.intent.toUri(Intent.URI_INTENT_SCHEME);
|
||||
if (intents.contains(intentUri)) {
|
||||
suggestions.remove(i);
|
||||
} else {
|
||||
intents.add(intentUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,498 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.settingslib.suggestions;
|
||||
|
||||
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 androidx.annotation.VisibleForTesting;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.util.Xml;
|
||||
import android.view.InflateException;
|
||||
|
||||
import com.android.settingslib.drawer.Tile;
|
||||
import com.android.settingslib.drawer.TileUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SuggestionParser {
|
||||
|
||||
private static final String TAG = "SuggestionParser";
|
||||
|
||||
// If defined, only returns this suggestion if the feature is supported.
|
||||
public static final String META_DATA_REQUIRE_FEATURE = "com.android.settings.require_feature";
|
||||
|
||||
// If defined, only display this optional step if an account of that type exists.
|
||||
private static final String META_DATA_REQUIRE_ACCOUNT = "com.android.settings.require_account";
|
||||
|
||||
// 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:
|
||||
* 0,10
|
||||
* Will appear immediately, but if the user removes it, it will come back after 10 days.
|
||||
*
|
||||
* Another example:
|
||||
* 10,30
|
||||
* Will only show up after 10 days, and then again after 30.
|
||||
*/
|
||||
public static final String META_DATA_DISMISS_CONTROL = "com.android.settings.dismiss";
|
||||
|
||||
// Shared prefs keys for storing dismissed state.
|
||||
// Index into current dismissed state.
|
||||
public static final String SETUP_TIME = "_setup_time";
|
||||
private static final String IS_DISMISSED = "_is_dismissed";
|
||||
|
||||
// Default dismiss control for smart suggestions.
|
||||
private static final String DEFAULT_SMART_DISMISS_CONTROL = "0";
|
||||
|
||||
private final Context mContext;
|
||||
private final List<SuggestionCategory> mSuggestionList;
|
||||
private final ArrayMap<Pair<String, String>, Tile> mAddCache = new ArrayMap<>();
|
||||
private final SharedPreferences mSharedPrefs;
|
||||
private final String mDefaultDismissControl;
|
||||
|
||||
public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml,
|
||||
String defaultDismissControl) {
|
||||
this(
|
||||
context,
|
||||
sharedPrefs,
|
||||
(List<SuggestionCategory>) new SuggestionOrderInflater(context).parse(orderXml),
|
||||
defaultDismissControl);
|
||||
}
|
||||
|
||||
public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml) {
|
||||
this(context, sharedPrefs, orderXml, DEFAULT_SMART_DISMISS_CONTROL);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public SuggestionParser(
|
||||
Context context,
|
||||
SharedPreferences sharedPrefs,
|
||||
List<SuggestionCategory> suggestionList,
|
||||
String defaultDismissControl) {
|
||||
mContext = context;
|
||||
mSuggestionList = suggestionList;
|
||||
mSharedPrefs = sharedPrefs;
|
||||
mDefaultDismissControl = defaultDismissControl;
|
||||
}
|
||||
|
||||
public SuggestionList getSuggestions(boolean isSmartSuggestionEnabled) {
|
||||
final SuggestionList suggestionList = new SuggestionList();
|
||||
final int N = mSuggestionList.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
final SuggestionCategory category = mSuggestionList.get(i);
|
||||
if (category.exclusive && !isExclusiveCategoryExpired(category)) {
|
||||
// If suggestions from an exclusive category are present, parsing is stopped
|
||||
// and only suggestions from that category are displayed. Note that subsequent
|
||||
// exclusive categories are also ignored.
|
||||
final List<Tile> exclusiveSuggestions = new ArrayList<>();
|
||||
|
||||
// Read suggestion and force isSmartSuggestion to be false so the rule defined
|
||||
// from each suggestion itself is used.
|
||||
readSuggestions(category, exclusiveSuggestions, false /* isSmartSuggestion */);
|
||||
if (!exclusiveSuggestions.isEmpty()) {
|
||||
final SuggestionList exclusiveList = new SuggestionList();
|
||||
exclusiveList.addSuggestions(category, exclusiveSuggestions);
|
||||
return exclusiveList;
|
||||
}
|
||||
} else {
|
||||
// Either the category is not exclusive, or the exclusiveness expired so we should
|
||||
// treat it as a normal category.
|
||||
final List<Tile> suggestions = new ArrayList<>();
|
||||
readSuggestions(category, suggestions, isSmartSuggestionEnabled);
|
||||
suggestionList.addSuggestions(category, suggestions);
|
||||
}
|
||||
}
|
||||
return suggestionList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses a suggestion, returns true if the suggestion has no more dismisses left and should
|
||||
* be disabled.
|
||||
*/
|
||||
public boolean dismissSuggestion(Tile suggestion) {
|
||||
final String keyBase = suggestion.intent.getComponent().flattenToShortString();
|
||||
mSharedPrefs.edit()
|
||||
.putBoolean(keyBase + IS_DISMISSED, true)
|
||||
.commit();
|
||||
return true;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void filterSuggestions(
|
||||
List<Tile> suggestions, int countBefore, boolean isSmartSuggestionEnabled) {
|
||||
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), isSmartSuggestionEnabled)) {
|
||||
suggestions.remove(i--);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void readSuggestions(
|
||||
SuggestionCategory category, List<Tile> suggestions, boolean isSmartSuggestionEnabled) {
|
||||
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, false, true /* shouldUpdateTiles */);
|
||||
filterSuggestions(suggestions, countBefore, isSmartSuggestionEnabled);
|
||||
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.
|
||||
Tile item = suggestions.remove(suggestions.size() - 1);
|
||||
while (suggestions.size() > countBefore) {
|
||||
Tile last = suggestions.remove(suggestions.size() - 1);
|
||||
if (last.priority > item.priority) {
|
||||
item = last;
|
||||
}
|
||||
}
|
||||
// If category is marked as done, do not add any item.
|
||||
if (!isCategoryDone(category.category)) {
|
||||
suggestions.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAvailable(Tile suggestion) {
|
||||
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) {
|
||||
final String requiredAccountType = suggestion.metaData.getString(META_DATA_REQUIRE_ACCOUNT);
|
||||
if (requiredAccountType == null) {
|
||||
return true;
|
||||
}
|
||||
AccountManager accountManager = mContext.getSystemService(AccountManager.class);
|
||||
Account[] accounts = accountManager.getAccountsByType(requiredAccountType);
|
||||
boolean satisfiesRequiredAccount = accounts.length > 0;
|
||||
if (!satisfiesRequiredAccount) {
|
||||
Log.i(TAG, suggestion.title + " requires unavailable account type "
|
||||
+ requiredAccountType);
|
||||
}
|
||||
return satisfiesRequiredAccount;
|
||||
}
|
||||
|
||||
public boolean isSupported(Tile suggestion) {
|
||||
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());
|
||||
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;
|
||||
} catch (Resources.NotFoundException e) {
|
||||
Log.w(TAG, "Cannot find resources for " + suggestion.intent.getComponent(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public void markCategoryDone(String category) {
|
||||
String name = Settings.Secure.COMPLETED_CATEGORY_PREFIX + category;
|
||||
Settings.Secure.putInt(mContext.getContentResolver(), name, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the category's exclusiveness has expired.
|
||||
*/
|
||||
private boolean isExclusiveCategoryExpired(SuggestionCategory category) {
|
||||
final String keySetupTime = category.category + SETUP_TIME;
|
||||
final long currentTime = System.currentTimeMillis();
|
||||
if (!mSharedPrefs.contains(keySetupTime)) {
|
||||
mSharedPrefs.edit()
|
||||
.putLong(keySetupTime, currentTime)
|
||||
.commit();
|
||||
}
|
||||
if (category.exclusiveExpireDaysInMillis < 0) {
|
||||
// negative means never expires
|
||||
return false;
|
||||
}
|
||||
final long setupTime = mSharedPrefs.getLong(keySetupTime, 0);
|
||||
final long elapsedTime = currentTime - setupTime;
|
||||
Log.d(TAG, "Day " + elapsedTime / DateUtils.DAY_IN_MILLIS + " for " + category.category);
|
||||
return elapsedTime > category.exclusiveExpireDaysInMillis;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean isDismissed(Tile suggestion, boolean isSmartSuggestionEnabled) {
|
||||
String dismissControl = getDismissControl(suggestion, isSmartSuggestionEnabled);
|
||||
String keyBase = suggestion.intent.getComponent().flattenToShortString();
|
||||
if (!mSharedPrefs.contains(keyBase + SETUP_TIME)) {
|
||||
mSharedPrefs.edit()
|
||||
.putLong(keyBase + SETUP_TIME, System.currentTimeMillis())
|
||||
.commit();
|
||||
}
|
||||
// Check if it's already manually dismissed
|
||||
final boolean isDismissed = mSharedPrefs.getBoolean(keyBase + IS_DISMISSED, false);
|
||||
if (isDismissed) {
|
||||
return true;
|
||||
}
|
||||
if (dismissControl == null) {
|
||||
return false;
|
||||
}
|
||||
// Parse when suggestion should first appear. return true to artificially hide suggestion
|
||||
// before then.
|
||||
int firstAppearDay = parseDismissString(dismissControl);
|
||||
long firstAppearDayInMs = getEndTime(mSharedPrefs.getLong(keyBase + SETUP_TIME, 0),
|
||||
firstAppearDay);
|
||||
if (System.currentTimeMillis() >= firstAppearDayInMs) {
|
||||
// Dismiss timeout has passed, undismiss it.
|
||||
mSharedPrefs.edit()
|
||||
.putBoolean(keyBase + IS_DISMISSED, false)
|
||||
.commit();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private long getEndTime(long startTime, int daysDelay) {
|
||||
long days = daysDelay * DateUtils.DAY_IN_MILLIS;
|
||||
return startTime + days;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the first int from a string formatted as "0,1,2..."
|
||||
* The value means suggestion should first appear on Day X.
|
||||
*/
|
||||
private int parseDismissString(String dismissControl) {
|
||||
final String[] dismissStrs = dismissControl.split(",");
|
||||
return Integer.parseInt(dismissStrs[0]);
|
||||
}
|
||||
|
||||
private String getDismissControl(Tile suggestion, boolean isSmartSuggestionEnabled) {
|
||||
if (isSmartSuggestionEnabled) {
|
||||
return mDefaultDismissControl;
|
||||
} else {
|
||||
return suggestion.metaData.getString(META_DATA_DISMISS_CONTROL);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SuggestionOrderInflater {
|
||||
private static final String TAG_LIST = "optional-steps";
|
||||
private static final String TAG_ITEM = "step";
|
||||
|
||||
private static final String ATTR_CATEGORY = "category";
|
||||
private static final String ATTR_PACKAGE = "package";
|
||||
private static final String ATTR_MULTIPLE = "multiple";
|
||||
private static final String ATTR_EXCLUSIVE = "exclusive";
|
||||
private static final String ATTR_EXCLUSIVE_EXPIRE_DAYS = "exclusiveExpireDays";
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
public SuggestionOrderInflater(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public Object parse(int resource) {
|
||||
XmlPullParser parser = mContext.getResources().getXml(resource);
|
||||
final AttributeSet attrs = Xml.asAttributeSet(parser);
|
||||
try {
|
||||
// Look for the root node.
|
||||
int type;
|
||||
do {
|
||||
type = parser.next();
|
||||
} while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT);
|
||||
|
||||
if (type != XmlPullParser.START_TAG) {
|
||||
throw new InflateException(parser.getPositionDescription()
|
||||
+ ": No start tag found!");
|
||||
}
|
||||
|
||||
// Temp is the root that was found in the xml
|
||||
Object xmlRoot = onCreateItem(parser.getName(), attrs);
|
||||
|
||||
// Inflate all children under temp
|
||||
rParse(parser, xmlRoot, attrs);
|
||||
return xmlRoot;
|
||||
} catch (XmlPullParserException | IOException e) {
|
||||
Log.w(TAG, "Problem parser resource " + resource, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive method used to descend down the xml hierarchy and instantiate
|
||||
* items, instantiate their children.
|
||||
*/
|
||||
private void rParse(XmlPullParser parser, Object parent, final AttributeSet attrs)
|
||||
throws XmlPullParserException, IOException {
|
||||
final int depth = parser.getDepth();
|
||||
|
||||
int type;
|
||||
while (((type = parser.next()) != XmlPullParser.END_TAG ||
|
||||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
|
||||
if (type != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String name = parser.getName();
|
||||
|
||||
Object item = onCreateItem(name, attrs);
|
||||
onAddChildItem(parent, item);
|
||||
rParse(parser, item, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onAddChildItem(Object parent, Object child) {
|
||||
if (parent instanceof List<?> && child instanceof SuggestionCategory) {
|
||||
((List<SuggestionCategory>) parent).add((SuggestionCategory) child);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Parent was not a list");
|
||||
}
|
||||
}
|
||||
|
||||
protected Object onCreateItem(String name, AttributeSet attrs) {
|
||||
if (name.equals(TAG_LIST)) {
|
||||
return new ArrayList<SuggestionCategory>();
|
||||
} else if (name.equals(TAG_ITEM)) {
|
||||
SuggestionCategory category = new SuggestionCategory();
|
||||
category.category = attrs.getAttributeValue(null, ATTR_CATEGORY);
|
||||
category.pkg = attrs.getAttributeValue(null, ATTR_PACKAGE);
|
||||
String multiple = attrs.getAttributeValue(null, ATTR_MULTIPLE);
|
||||
category.multiple = !TextUtils.isEmpty(multiple) && Boolean.parseBoolean(multiple);
|
||||
String exclusive = attrs.getAttributeValue(null, ATTR_EXCLUSIVE);
|
||||
category.exclusive =
|
||||
!TextUtils.isEmpty(exclusive) && Boolean.parseBoolean(exclusive);
|
||||
String expireDaysAttr = attrs.getAttributeValue(null,
|
||||
ATTR_EXCLUSIVE_EXPIRE_DAYS);
|
||||
long expireDays = !TextUtils.isEmpty(expireDaysAttr)
|
||||
? Integer.parseInt(expireDaysAttr)
|
||||
: -1;
|
||||
category.exclusiveExpireDaysInMillis = DateUtils.DAY_IN_MILLIS * expireDays;
|
||||
return category;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown item " + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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.
|
||||
-->
|
||||
|
||||
<optional-steps>
|
||||
<step category="com.android.settings.suggested.category.DEFERRED_SETUP"
|
||||
exclusive="true" />
|
||||
<step category="com.android.settings.suggested.category.LOCK_SCREEN" />
|
||||
<step category="com.android.settings.suggested.category.TRUST_AGENT" />
|
||||
<step category="com.android.settings.suggested.category.EMAIL" />
|
||||
<step category="com.android.settings.suggested.category.PARTNER_ACCOUNT"
|
||||
multiple="true" />
|
||||
<step category="com.android.settings.suggested.category.GESTURE" />
|
||||
<step category="com.android.settings.suggested.category.HOTWORD" />
|
||||
<step category="com.android.settings.suggested.category.DEFAULT"
|
||||
multiple="true" />
|
||||
<step category="com.android.settings.suggested.category.SETTINGS_ONLY"
|
||||
multiple="true" />
|
||||
</optional-steps>
|
||||
@@ -28,7 +28,6 @@ import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.robolectric.RuntimeEnvironment.application;
|
||||
import static org.robolectric.shadow.api.Shadow.extract;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.ContentResolver;
|
||||
@@ -52,9 +51,6 @@ import android.util.ArrayMap;
|
||||
import android.util.Pair;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.android.settingslib.R;
|
||||
import com.android.settingslib.suggestions.SuggestionParser;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -67,7 +63,6 @@ import org.robolectric.annotation.Implementation;
|
||||
import org.robolectric.annotation.Implements;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -163,35 +158,6 @@ public class TileUtilsTest {
|
||||
assertThat(outTiles.isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTilesForIntent_shouldSkipFilteredApps() {
|
||||
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 */, true /* forceTintExternalIcon */);
|
||||
|
||||
assertThat(outTiles.size()).isEqualTo(1);
|
||||
SuggestionParser parser = new SuggestionParser(
|
||||
mContext,
|
||||
null,
|
||||
Collections.emptyList(),
|
||||
"0,10");
|
||||
parser.filterSuggestions(outTiles, 0, false);
|
||||
assertThat(outTiles.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCategories_shouldHandleExtraIntentAction() {
|
||||
final String testCategory = "category1";
|
||||
@@ -392,108 +358,6 @@ public class TileUtilsTest {
|
||||
assertThat(outTiles.size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTilesForIntent_shouldShowRemoteViewIfSpecified() {
|
||||
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 */);
|
||||
resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
|
||||
R.layout.user_preference);
|
||||
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 */, true /* forceTintExternalIcon */);
|
||||
|
||||
assertThat(outTiles.size()).isEqualTo(1);
|
||||
Tile tile = outTiles.get(0);
|
||||
assertThat(tile.remoteViews).isNotNull();
|
||||
assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTilesForIntent_summaryUriSpecified_shouldOverrideRemoteViewSummary()
|
||||
throws RemoteException {
|
||||
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,
|
||||
null, URI_GET_SUMMARY);
|
||||
resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
|
||||
R.layout.user_preference);
|
||||
info.add(resolveInfo);
|
||||
|
||||
when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
|
||||
.thenReturn(info);
|
||||
|
||||
// Mock the content provider interaction.
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, "new summary text");
|
||||
when(mIContentProvider.call(anyString(),
|
||||
eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY),
|
||||
any())).thenReturn(bundle);
|
||||
when(mContentResolver.acquireUnstableProvider(anyString()))
|
||||
.thenReturn(mIContentProvider);
|
||||
when(mContentResolver.acquireUnstableProvider(any(Uri.class)))
|
||||
.thenReturn(mIContentProvider);
|
||||
|
||||
TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
|
||||
null /* defaultCategory */, outTiles, false /* usePriority */,
|
||||
false /* checkCategory */, true /* forceTintExternalIcon */);
|
||||
|
||||
assertThat(outTiles.size()).isEqualTo(1);
|
||||
Tile tile = outTiles.get(0);
|
||||
assertThat(tile.remoteViews).isNotNull();
|
||||
assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
|
||||
// Make sure the summary TextView got a new text string.
|
||||
TileUtilsShadowRemoteViews shadowRemoteViews = extract(tile.remoteViews);
|
||||
assertThat(shadowRemoteViews.overrideViewId).isEqualTo(android.R.id.summary);
|
||||
assertThat(shadowRemoteViews.overrideText).isEqualTo("new summary text");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTilesForIntent_providerUnavailable_shouldNotOverrideRemoteViewSummary()
|
||||
throws RemoteException {
|
||||
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,
|
||||
null, URI_GET_SUMMARY);
|
||||
resolveInfo.activityInfo.metaData.putInt("com.android.settings.custom_view",
|
||||
R.layout.user_preference);
|
||||
info.add(resolveInfo);
|
||||
|
||||
when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
|
||||
.thenReturn(info);
|
||||
|
||||
// Mock the content provider interaction.
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY, "new summary text");
|
||||
when(mIContentProvider.call(anyString(),
|
||||
eq(TileUtils.getMethodFromUri(Uri.parse(URI_GET_SUMMARY))), eq(URI_GET_SUMMARY),
|
||||
any())).thenReturn(bundle);
|
||||
|
||||
TileUtils.getTilesForIntent(mContext, UserHandle.CURRENT, intent, addedCache,
|
||||
null /* defaultCategory */, outTiles, false /* usePriority */,
|
||||
false /* checkCategory */, true /* forceTintExternalIcon */);
|
||||
|
||||
assertThat(outTiles.size()).isEqualTo(1);
|
||||
Tile tile = outTiles.get(0);
|
||||
assertThat(tile.remoteViews).isNotNull();
|
||||
assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
|
||||
// Make sure the summary TextView didn't get any text view updates.
|
||||
TileUtilsShadowRemoteViews shadowRemoteViews = extract(tile.remoteViews);
|
||||
assertThat(shadowRemoteViews.overrideViewId).isNull();
|
||||
assertThat(shadowRemoteViews.overrideText).isNull();
|
||||
}
|
||||
|
||||
public static ResolveInfo newInfo(boolean systemApp, String category) {
|
||||
return newInfo(systemApp, category, null);
|
||||
}
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.settingslib.suggestions;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.robolectric.RuntimeEnvironment.application;
|
||||
import static org.robolectric.shadow.api.Shadow.extract;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import com.android.settingslib.SettingsLibRobolectricTestRunner;
|
||||
import com.android.settingslib.drawer.Tile;
|
||||
import com.android.settingslib.drawer.TileUtilsTest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.shadows.ShadowPackageManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(SettingsLibRobolectricTestRunner.class)
|
||||
public class SuggestionParserTest {
|
||||
|
||||
private ShadowPackageManager mPackageManager;
|
||||
private SuggestionParser mSuggestionParser;
|
||||
private SuggestionCategory mMultipleCategory;
|
||||
private SuggestionCategory mExclusiveCategory;
|
||||
private SuggestionCategory mExpiredExclusiveCategory;
|
||||
private List<Tile> mSuggestionsBeforeDismiss;
|
||||
private List<Tile> mSuggestionsAfterDismiss;
|
||||
private SharedPreferences mPrefs;
|
||||
private Tile mSuggestion;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mPackageManager = extract(application.getPackageManager());
|
||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(application);
|
||||
mSuggestion = new Tile();
|
||||
mSuggestion.intent = new Intent("action");
|
||||
mSuggestion.intent.setComponent(new ComponentName("pkg", "cls"));
|
||||
mSuggestion.metaData = new Bundle();
|
||||
mMultipleCategory = new SuggestionCategory();
|
||||
mMultipleCategory.category = "category1";
|
||||
mMultipleCategory.multiple = true;
|
||||
mExclusiveCategory = new SuggestionCategory();
|
||||
mExclusiveCategory.category = "category2";
|
||||
mExclusiveCategory.exclusive = true;
|
||||
mExpiredExclusiveCategory = new SuggestionCategory();
|
||||
mExpiredExclusiveCategory.category = "category3";
|
||||
mExpiredExclusiveCategory.exclusive = true;
|
||||
mExpiredExclusiveCategory.exclusiveExpireDaysInMillis = 0;
|
||||
|
||||
mSuggestionParser = new SuggestionParser(application, mPrefs,
|
||||
Arrays.asList(mMultipleCategory, mExclusiveCategory, mExpiredExclusiveCategory),
|
||||
"0");
|
||||
|
||||
ResolveInfo info1 = TileUtilsTest.newInfo(true, null);
|
||||
info1.activityInfo.packageName = "pkg";
|
||||
ResolveInfo infoDupe1 = TileUtilsTest.newInfo(true, null);
|
||||
infoDupe1.activityInfo.packageName = "pkg";
|
||||
|
||||
ResolveInfo info2 = TileUtilsTest.newInfo(true, null);
|
||||
info2.activityInfo.packageName = "pkg2";
|
||||
ResolveInfo info3 = TileUtilsTest.newInfo(true, null);
|
||||
info3.activityInfo.packageName = "pkg3";
|
||||
ResolveInfo info4 = TileUtilsTest.newInfo(true, null);
|
||||
info4.activityInfo.packageName = "pkg4";
|
||||
|
||||
Intent intent1 = new Intent(Intent.ACTION_MAIN).addCategory("category1");
|
||||
Intent intent2 = new Intent(Intent.ACTION_MAIN).addCategory("category2");
|
||||
Intent intent3 = new Intent(Intent.ACTION_MAIN).addCategory("category3");
|
||||
|
||||
mPackageManager.addResolveInfoForIntent(intent1, info1);
|
||||
mPackageManager.addResolveInfoForIntent(intent1, info2);
|
||||
mPackageManager.addResolveInfoForIntent(intent1, infoDupe1);
|
||||
mPackageManager.addResolveInfoForIntent(intent2, info3);
|
||||
mPackageManager.addResolveInfoForIntent(intent3, info4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dismissSuggestion_shouldDismiss() {
|
||||
assertThat(mSuggestionParser.dismissSuggestion(mSuggestion)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSuggestions_withoutSmartSuggestions_shouldDismiss() {
|
||||
readAndDismissSuggestion(false);
|
||||
mSuggestionParser.readSuggestions(mMultipleCategory, mSuggestionsAfterDismiss, false);
|
||||
assertThat(mSuggestionsBeforeDismiss).hasSize(2);
|
||||
assertThat(mSuggestionsAfterDismiss).hasSize(1);
|
||||
assertThat(mSuggestionsBeforeDismiss.get(1)).isEqualTo(mSuggestionsAfterDismiss.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSuggestions_withSmartSuggestions_shouldDismiss() {
|
||||
readAndDismissSuggestion(true);
|
||||
assertThat(mSuggestionsBeforeDismiss).hasSize(2);
|
||||
assertThat(mSuggestionsAfterDismiss).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSuggestion_exclusiveNotAvailable_onlyRegularCategoryAndNoDupe() {
|
||||
mPackageManager.removeResolveInfosForIntent(
|
||||
new Intent(Intent.ACTION_MAIN).addCategory("category2"),
|
||||
"pkg3");
|
||||
mPackageManager.removeResolveInfosForIntent(
|
||||
new Intent(Intent.ACTION_MAIN).addCategory("category3"),
|
||||
"pkg4");
|
||||
|
||||
// If exclusive item is not available, the other categories should be shown
|
||||
final SuggestionList sl =
|
||||
mSuggestionParser.getSuggestions(false /* isSmartSuggestionEnabled */);
|
||||
final List<Tile> suggestions = sl.getSuggestions();
|
||||
assertThat(suggestions).hasSize(2);
|
||||
|
||||
assertThat(suggestions.get(0).intent.getComponent().getPackageName()).isEqualTo("pkg");
|
||||
assertThat(suggestions.get(1).intent.getComponent().getPackageName()).isEqualTo("pkg2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSuggestion_exclusiveExpiredAvailable_shouldLoadWithRegularCategory() {
|
||||
// First remove permanent exclusive
|
||||
mPackageManager.removeResolveInfosForIntent(
|
||||
new Intent(Intent.ACTION_MAIN).addCategory("category2"),
|
||||
"pkg3");
|
||||
// Set the other exclusive to be expired.
|
||||
mPrefs.edit()
|
||||
.putLong(mExpiredExclusiveCategory.category + "_setup_time",
|
||||
System.currentTimeMillis() - 1000)
|
||||
.commit();
|
||||
|
||||
// If exclusive is expired, they should be shown together with the other categories
|
||||
final SuggestionList sl =
|
||||
mSuggestionParser.getSuggestions(true /* isSmartSuggestionEnabled */);
|
||||
final List<Tile> suggestions = sl.getSuggestions();
|
||||
|
||||
assertThat(suggestions).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSuggestions_exclusive() {
|
||||
final SuggestionList sl =
|
||||
mSuggestionParser.getSuggestions(false /* isSmartSuggestionEnabled */);
|
||||
final List<Tile> suggestions = sl.getSuggestions();
|
||||
|
||||
assertThat(suggestions).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSuggestionDismissed_dismissedSuggestion_shouldReturnTrue() {
|
||||
final Tile suggestion = new Tile();
|
||||
suggestion.metaData = new Bundle();
|
||||
suggestion.metaData.putString(SuggestionParser.META_DATA_DISMISS_CONTROL, "1,2,3");
|
||||
suggestion.intent = new Intent().setComponent(new ComponentName("pkg", "cls"));
|
||||
|
||||
// Dismiss suggestion when smart suggestion is not enabled.
|
||||
mSuggestionParser.dismissSuggestion(suggestion);
|
||||
|
||||
assertThat(mSuggestionParser.isDismissed(suggestion, true /* isSmartSuggestionEnabled */))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
private void readAndDismissSuggestion(boolean isSmartSuggestionEnabled) {
|
||||
mSuggestionsBeforeDismiss = new ArrayList<>();
|
||||
mSuggestionsAfterDismiss = new ArrayList<>();
|
||||
mSuggestionParser.readSuggestions(
|
||||
mMultipleCategory, mSuggestionsBeforeDismiss, isSmartSuggestionEnabled);
|
||||
|
||||
final Tile suggestion = mSuggestionsBeforeDismiss.get(0);
|
||||
if (mSuggestionParser.dismissSuggestion(suggestion)) {
|
||||
mPackageManager.removeResolveInfosForIntent(
|
||||
new Intent(Intent.ACTION_MAIN).addCategory(mMultipleCategory.category),
|
||||
suggestion.intent.getComponent().getPackageName());
|
||||
}
|
||||
mSuggestionParser.readSuggestions(
|
||||
mMultipleCategory, mSuggestionsAfterDismiss, isSmartSuggestionEnabled);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user