Merge "Do not start suggestion loader if host isn't attached."
diff --git a/res/layout/app_preference_item.xml b/res/layout/app_preference_item.xml
index f13ced7..5653920 100755
--- a/res/layout/app_preference_item.xml
+++ b/res/layout/app_preference_item.xml
@@ -27,11 +27,10 @@
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<FrameLayout
android:id="@+id/icon_frame"
- style="@style/preference_icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
- android:paddingEnd="12dp"
+ android:minWidth="56dp"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<android.support.v7.internal.widget.PreferenceImageView
diff --git a/res/layout/preference_small_icon.xml b/res/layout/preference_small_icon.xml
deleted file mode 100644
index e734b4b..0000000
--- a/res/layout/preference_small_icon.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
- -->
-
-<!-- Same as preference_material except that it uses a 24dp fixed size icon -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:gravity="center_vertical"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:background="?android:attr/selectableItemBackground"
- android:clipToPadding="false"
- android:focusable="true" >
-
- <LinearLayout
- android:id="@+id/icon_frame"
- style="@style/preference_icon_frame"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="start|center_vertical"
- android:orientation="horizontal"
- android:paddingEnd="12dp"
- android:paddingTop="4dp"
- android:paddingBottom="4dp">
- <android.support.v7.internal.widget.PreferenceImageView
- android:id="@android:id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:maxWidth="24dp"
- app:maxHeight="24dp" />
- </LinearLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingTop="16dp"
- android:paddingBottom="16dp">
-
- <TextView android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceListItem"
- android:ellipsize="marquee" />
-
- <TextView android:id="@android:id/summary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@android:id/title"
- android:layout_alignStart="@android:id/title"
- android:textAppearance="?android:attr/textAppearanceListItemSecondary"
- android:textColor="?android:attr/textColorSecondary"
- android:maxLines="10" />
-
- </RelativeLayout>
-
- <!-- Preference should place its actual preference widget here. -->
- <LinearLayout android:id="@android:id/widget_frame"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="end|center_vertical"
- android:paddingStart="16dp"
- android:orientation="vertical" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/storage_item.xml b/res/layout/storage_item.xml
index 4b14bb3..ba1c697 100644
--- a/res/layout/storage_item.xml
+++ b/res/layout/storage_item.xml
@@ -36,12 +36,11 @@
<LinearLayout
android:id="@+id/icon_frame"
- style="@style/preference_icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
+ android:minWidth="56dp"
android:orientation="horizontal"
- android:paddingEnd="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<com.android.internal.widget.PreferenceImageView
diff --git a/res/layout/storage_volume.xml b/res/layout/storage_volume.xml
index 39a6f85..ca368b9 100644
--- a/res/layout/storage_volume.xml
+++ b/res/layout/storage_volume.xml
@@ -26,12 +26,11 @@
<LinearLayout
android:id="@+id/icon_frame"
- style="@style/preference_icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
+ android:minWidth="56dp"
android:orientation="horizontal"
- android:paddingEnd="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<com.android.internal.widget.PreferenceImageView
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f0721fc..8b84849 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6471,7 +6471,7 @@
<string name="dashboard_suggestion_condition_footer_content_description">Collapse</string>
<!-- Title for setting tile leading to network and Internet settings [CHAR LIMIT=40]-->
- <string name="network_dashboard_title">Network & Internet</string>
+ <string name="network_dashboard_title">Network & internet</string>
<!-- Summary for Network and Internet settings, explaining it contains mobile network setting [CHAR LIMIT=NONE]-->
<string name="network_dashboard_summary_mobile">mobile</string>
<!-- Summary for Network and Internet settings, explaining it contains network data usage setting [CHAR LIMIT=NONE]-->
diff --git a/res/xml/security_settings_misc.xml b/res/xml/security_settings_misc.xml
index ee2c534..6a71033 100644
--- a/res/xml/security_settings_misc.xml
+++ b/res/xml/security_settings_misc.xml
@@ -64,7 +64,7 @@
android:key="encryption_and_credential"
android:title="@string/encryption_and_credential_settings_title"
android:summary="@string/encryption_and_credential_settings_summary"
- android:fragment="com.android.settings.EncryptionAndCredential"/>
+ android:fragment="com.android.settings.security.EncryptionAndCredential"/>
<Preference android:key="manage_trust_agents"
android:title="@string/manage_trust_agents"
diff --git a/src/com/android/settings/accounts/AccountPreferenceBase.java b/src/com/android/settings/accounts/AccountPreferenceBase.java
index 7c3b54c..54ddf64 100644
--- a/src/com/android/settings/accounts/AccountPreferenceBase.java
+++ b/src/com/android/settings/accounts/AccountPreferenceBase.java
@@ -23,18 +23,16 @@
import android.content.SyncStatusObserver;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
-import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
-import android.support.v7.preference.PreferenceScreen;
import android.text.format.DateFormat;
import android.util.Log;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
import com.android.settingslib.accounts.AuthenticatorHelper;
+import com.android.settingslib.utils.ThreadUtils;
-import java.util.ArrayList;
import java.util.Date;
abstract class AccountPreferenceBase extends SettingsPreferenceFragment
@@ -46,8 +44,6 @@
public static final String AUTHORITIES_FILTER_KEY = "authorities";
public static final String ACCOUNT_TYPES_FILTER_KEY = "account_types";
- private final Handler mHandler = new Handler();
-
private UserManager mUm;
private Object mStatusChangeListenerHandle;
protected AuthenticatorHelper mAuthenticatorHelper;
@@ -118,29 +114,8 @@
ContentResolver.removeStatusChangeListener(mStatusChangeListenerHandle);
}
- private SyncStatusObserver mSyncStatusObserver = new SyncStatusObserver() {
- public void onStatusChanged(int which) {
- mHandler.post(new Runnable() {
- public void run() {
- onSyncStateUpdated();
- }
- });
- }
- };
-
- public ArrayList<String> getAuthoritiesForAccountType(String type) {
- return mAuthenticatorHelper.getAuthoritiesForAccountType(type);
- }
-
- /**
- * Gets the preferences.xml file associated with a particular account type.
- * @param accountType the type of account
- * @return a PreferenceScreen inflated from accountPreferenceId.
- */
- public PreferenceScreen addPreferencesForType(final String accountType,
- PreferenceScreen parent) {
- return mAccountTypePreferenceLoader.addPreferencesForType(accountType, parent);
- }
+ private SyncStatusObserver mSyncStatusObserver =
+ which -> ThreadUtils.postOnMainThread(() -> onSyncStateUpdated());
public void updateAuthDescriptions() {
mAuthenticatorHelper.updateAuthDescriptions(getActivity());
diff --git a/src/com/android/settings/applications/RunningServiceDetails.java b/src/com/android/settings/applications/RunningServiceDetails.java
index 84c2ee5..7e73a9b 100644
--- a/src/com/android/settings/applications/RunningServiceDetails.java
+++ b/src/com/android/settings/applications/RunningServiceDetails.java
@@ -21,7 +21,6 @@
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Debug;
-import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
@@ -37,6 +36,7 @@
import com.android.settings.Utils;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settingslib.utils.ThreadUtils;
import java.io.File;
import java.io.FileInputStream;
@@ -481,15 +481,12 @@
addDetailViews();
}
}
-
+
private void finish() {
- (new Handler()).post(new Runnable() {
- @Override
- public void run() {
- Activity a = getActivity();
- if (a != null) {
- a.onBackPressed();
- }
+ ThreadUtils.postOnMainThread(() -> {
+ final Activity a = getActivity();
+ if (a != null) {
+ a.onBackPressed();
}
});
}
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java b/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java
index 00dedf9..97f6d4b 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAutofillPicker.java
@@ -27,7 +27,6 @@
import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
import android.provider.Settings;
import android.service.autofill.AutofillService;
import android.service.autofill.AutofillServiceInfo;
@@ -39,6 +38,7 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
+import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.List;
@@ -59,7 +59,6 @@
* Set when the fragment is implementing ACTION_REQUEST_SET_AUTOFILL_SERVICE.
*/
private DialogInterface.OnClickListener mCancelListener;
- private final Handler mHandler = new Handler();
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -123,17 +122,17 @@
private final PackageMonitor mSettingsPackageMonitor = new PackageMonitor() {
@Override
public void onPackageAdded(String packageName, int uid) {
- mHandler.post(() -> update());
+ ThreadUtils.postOnMainThread(() -> update());
}
@Override
public void onPackageModified(String packageName) {
- mHandler.post(() -> update());
+ ThreadUtils.postOnMainThread(() -> update());
}
@Override
public void onPackageRemoved(String packageName, int uid) {
- mHandler.post(() -> update());
+ ThreadUtils.postOnMainThread(() -> update());
}
};
diff --git a/src/com/android/settings/core/InstrumentedPreferenceFragment.java b/src/com/android/settings/core/InstrumentedPreferenceFragment.java
index 8ef8c0e..5b95d66 100644
--- a/src/com/android/settings/core/InstrumentedPreferenceFragment.java
+++ b/src/com/android/settings/core/InstrumentedPreferenceFragment.java
@@ -85,7 +85,7 @@
}
public static boolean usePreferenceScreenTitle() {
- return FeatureFlagUtils.isEnabled(FEATURE_FLAG_USE_PREFERENCE_SCREEN_TITLE);
+ return FeatureFlagUtils.isEnabled(FEATURE_FLAG_USE_PREFERENCE_SCREEN_TITLE) || true;
}
protected final Context getPrefContext() {
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index e7f36e1..6796c26 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -183,6 +183,9 @@
}
}
+ @Override
+ protected abstract int getPreferenceScreenResId();
+
protected <T extends AbstractPreferenceController> T getPreferenceController(Class<T> clazz) {
AbstractPreferenceController controller = mPreferenceControllers.get(clazz);
return (T) controller;
diff --git a/src/com/android/settings/dashboard/SummaryLoader.java b/src/com/android/settings/dashboard/SummaryLoader.java
index 4586a55..5816bba8 100644
--- a/src/com/android/settings/dashboard/SummaryLoader.java
+++ b/src/com/android/settings/dashboard/SummaryLoader.java
@@ -35,6 +35,7 @@
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
+import com.android.settingslib.utils.ThreadUtils;
import java.lang.reflect.Field;
import java.util.List;
@@ -52,7 +53,6 @@
private final String mCategoryKey;
private final Worker mWorker;
- private final Handler mHandler;
private final HandlerThread mWorkerThread;
private SummaryConsumer mSummaryConsumer;
@@ -64,7 +64,6 @@
mDashboardFeatureProvider = FeatureFactory.getFactory(activity)
.getDashboardFeatureProvider(activity);
mCategoryKey = null;
- mHandler = new Handler();
mWorkerThread = new HandlerThread("SummaryLoader", Process.THREAD_PRIORITY_BACKGROUND);
mWorkerThread.start();
mWorker = new Worker(mWorkerThread.getLooper());
@@ -82,7 +81,6 @@
mDashboardFeatureProvider = FeatureFactory.getFactory(activity)
.getDashboardFeatureProvider(activity);
mCategoryKey = categoryKey;
- mHandler = new Handler();
mWorkerThread = new HandlerThread("SummaryLoader", Process.THREAD_PRIORITY_BACKGROUND);
mWorkerThread.start();
mWorker = new Worker(mWorkerThread.getLooper());
@@ -112,25 +110,22 @@
public void setSummary(SummaryProvider provider, final CharSequence summary) {
final ComponentName component = mSummaryProviderMap.get(provider);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
+ ThreadUtils.postOnMainThread(() -> {
- final Tile tile = getTileFromCategory(
- mDashboardFeatureProvider.getTilesForCategory(mCategoryKey), component);
+ final Tile tile = getTileFromCategory(
+ mDashboardFeatureProvider.getTilesForCategory(mCategoryKey), component);
- if (tile == null) {
- if (DEBUG) {
- Log.d(TAG, "Can't find tile for " + component);
- }
- return;
- }
+ if (tile == null) {
if (DEBUG) {
- Log.d(TAG, "setSummary " + tile.title + " - " + summary);
+ Log.d(TAG, "Can't find tile for " + component);
}
-
- updateSummaryIfNeeded(tile, summary);
+ return;
}
+ if (DEBUG) {
+ Log.d(TAG, "setSummary " + tile.title + " - " + summary);
+ }
+
+ updateSummaryIfNeeded(tile, summary);
});
}
diff --git a/src/com/android/settings/dashboard/SupportFragment.java b/src/com/android/settings/dashboard/SupportFragment.java
index 8a1a79b..fcc9f78 100644
--- a/src/com/android/settings/dashboard/SupportFragment.java
+++ b/src/com/android/settings/dashboard/SupportFragment.java
@@ -27,8 +27,6 @@
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
@@ -40,6 +38,7 @@
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.overlay.SupportFeatureProvider;
+import com.android.settingslib.utils.ThreadUtils;
/**
* Fragment for support tab in SettingsGoogle.
@@ -47,7 +46,6 @@
public final class SupportFragment extends InstrumentedFragment implements View.OnClickListener,
OnAccountsUpdateListener {
- private final Handler mHandler = new Handler(Looper.getMainLooper());
private final ConnectivityManager.NetworkCallback mNetworkCallback =
new ConnectivityManager.NetworkCallback() {
@@ -152,12 +150,9 @@
}
private void postConnectivityChanged() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mSupportItemAdapter != null) {
- mSupportItemAdapter.setHasInternet(hasInternet());
- }
+ ThreadUtils.postOnMainThread(() -> {
+ if (mSupportItemAdapter != null) {
+ mSupportItemAdapter.setHasInternet(hasInternet());
}
});
}
diff --git a/src/com/android/settings/datausage/DataSaverBackend.java b/src/com/android/settings/datausage/DataSaverBackend.java
index f37a3c3..041a81f 100644
--- a/src/com/android/settings/datausage/DataSaverBackend.java
+++ b/src/com/android/settings/datausage/DataSaverBackend.java
@@ -14,24 +14,23 @@
package com.android.settings.datausage;
+import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
+import static android.net.NetworkPolicyManager.POLICY_NONE;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+
import android.content.Context;
import android.net.INetworkPolicyListener;
import android.net.NetworkPolicyManager;
-import android.os.Handler;
-import android.os.Looper;
import android.os.RemoteException;
import android.util.SparseIntArray;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList;
-import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
-import static android.net.NetworkPolicyManager.POLICY_NONE;
-import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
-
public class DataSaverBackend {
private static final String TAG = "DataSaverBackend";
@@ -39,7 +38,6 @@
private final Context mContext;
private final MetricsFeatureProvider mMetricsFeatureProvider;
- private final Handler mHandler = new Handler(Looper.getMainLooper());
private final NetworkPolicyManager mPolicyManager;
private final ArrayList<Listener> mListeners = new ArrayList<>();
private SparseIntArray mUidPolicies = new SparseIntArray();
@@ -195,7 +193,7 @@
@Override
public void onUidPoliciesChanged(final int uid, final int uidPolicies) {
- mHandler.post(() -> handleUidPoliciesChanged(uid, uidPolicies));
+ ThreadUtils.postOnMainThread(() -> handleUidPoliciesChanged(uid, uidPolicies));
}
@Override
@@ -204,7 +202,7 @@
@Override
public void onRestrictBackgroundChanged(final boolean isDataSaving) throws RemoteException {
- mHandler.post(() -> handleRestrictBackgroundChanged(isDataSaving));
+ ThreadUtils.postOnMainThread(() -> handleRestrictBackgroundChanged(isDataSaving));
}
};
diff --git a/src/com/android/settings/display/ShowOperatorNamePreferenceController.java b/src/com/android/settings/display/ShowOperatorNamePreferenceController.java
index 8341519..4573c6d 100644
--- a/src/com/android/settings/display/ShowOperatorNamePreferenceController.java
+++ b/src/com/android/settings/display/ShowOperatorNamePreferenceController.java
@@ -19,10 +19,11 @@
import android.support.v7.preference.Preference;
import com.android.settings.R;
+import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
public class ShowOperatorNamePreferenceController extends AbstractPreferenceController
- implements Preference.OnPreferenceChangeListener {
+ implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
private static final String KEY_SHOW_OPERATOR_NAME = "show_operator_name";
diff --git a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceController.java b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceController.java
index 03a78ea..0927299 100644
--- a/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceController.java
+++ b/src/com/android/settings/enterprise/EnterpriseSetDefaultAppsListPreferenceController.java
@@ -20,7 +20,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-import android.os.Handler;
import android.os.UserHandle;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
@@ -36,6 +35,7 @@
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.users.UserFeatureProvider;
import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.Collections;
@@ -100,7 +100,7 @@
userMap.put(typeOfDefault, applicationInfos);
}
}
- new Handler(mContext.getMainLooper()).post(() -> { updateUi(); });
+ ThreadUtils.postOnMainThread(() -> updateUi());
}
@Override
diff --git a/src/com/android/settings/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/fingerprint/FingerprintEnrollEnrolling.java
index 10bc9e3..756f826 100644
--- a/src/com/android/settings/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/fingerprint/FingerprintEnrollEnrolling.java
@@ -32,6 +32,8 @@
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.os.UserHandle;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.text.TextUtils;
import android.view.MotionEvent;
import android.view.View;
@@ -74,6 +76,9 @@
*/
private static final int ICON_TOUCH_COUNT_SHOW_UNTIL_DIALOG_SHOWN = 3;
+ private static final VibrationEffect VIBRATE_EFFECT_ERROR =
+ VibrationEffect.createWaveform(new long[] {0, 5, 55, 60}, -1);
+
private ProgressBar mProgressBar;
private ObjectAnimator mProgressAnim;
private TextView mStartMessage;
@@ -90,6 +95,7 @@
private int mIndicatorBackgroundRestingColor;
private int mIndicatorBackgroundActivatedColor;
private boolean mRestoring;
+ private Vibrator mVibrator;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -100,6 +106,7 @@
mRepeatMessage = (TextView) findViewById(R.id.repeat_message);
mErrorText = (TextView) findViewById(R.id.error_text);
mProgressBar = (ProgressBar) findViewById(R.id.fingerprint_progress_bar);
+ mVibrator = getSystemService(Vibrator.class);
Button skipButton = findViewById(R.id.skip_button);
skipButton.setOnClickListener(this);
@@ -368,6 +375,9 @@
mErrorText.setAlpha(1f);
mErrorText.setTranslationY(0f);
}
+ if (isResumed()) {
+ mVibrator.vibrate(VIBRATE_EFFECT_ERROR);
+ }
}
private void clearError() {
@@ -378,12 +388,7 @@
R.dimen.fingerprint_error_text_disappear_distance))
.setDuration(100)
.setInterpolator(mFastOutLinearInInterpolator)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mErrorText.setVisibility(View.INVISIBLE);
- }
- })
+ .withEndAction(() -> mErrorText.setVisibility(View.INVISIBLE))
.start();
}
}
diff --git a/src/com/android/settings/network/VpnPreferenceController.java b/src/com/android/settings/network/VpnPreferenceController.java
index 763fb72..95513e0 100644
--- a/src/com/android/settings/network/VpnPreferenceController.java
+++ b/src/com/android/settings/network/VpnPreferenceController.java
@@ -23,8 +23,6 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
-import android.os.Handler;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -45,6 +43,7 @@
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.utils.ThreadUtils;
import java.util.List;
@@ -157,7 +156,7 @@
} else {
summary = getNameForVpnConfig(vpn, UserHandle.of(uid));
}
- new Handler(Looper.getMainLooper()).post(() -> mPreference.setSummary(summary));
+ ThreadUtils.postOnMainThread(() -> mPreference.setSummary(summary));
}
private String getNameForVpnConfig(VpnConfig cfg, UserHandle user) {
diff --git a/src/com/android/settings/search/AccessibilityServiceResultLoader.java b/src/com/android/settings/search/AccessibilityServiceResultLoader.java
index 345ab30..327aef2 100644
--- a/src/com/android/settings/search/AccessibilityServiceResultLoader.java
+++ b/src/com/android/settings/search/AccessibilityServiceResultLoader.java
@@ -30,105 +30,112 @@
import android.support.annotation.VisibleForTesting;
import android.support.v4.content.ContextCompat;
import android.util.IconDrawableFactory;
+import android.util.Log;
import android.view.accessibility.AccessibilityManager;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilitySettings;
import com.android.settings.dashboard.SiteMapManager;
-import com.android.settings.utils.AsyncLoader;
-import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
-public class AccessibilityServiceResultLoader extends AsyncLoader<Set<? extends SearchResult>> {
+public class AccessibilityServiceResultLoader extends
+ FutureTask<List<? extends SearchResult>> {
- private static final int NAME_NO_MATCH = -1;
-
- private final Context mContext;
-
- private List<String> mBreadcrumb;
- private SiteMapManager mSiteMapManager;
- @VisibleForTesting
- final String mQuery;
- private final AccessibilityManager mAccessibilityManager;
- private final PackageManager mPackageManager;
- private final int mUserId;
-
+ private static final String TAG = "A11yResultFutureTask";
public AccessibilityServiceResultLoader(Context context, String query,
- SiteMapManager mapManager) {
- super(context);
- mContext = context;
- mUserId = UserHandle.myUserId();
- mSiteMapManager = mapManager;
- mPackageManager = context.getPackageManager();
- mAccessibilityManager =
- (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mQuery = query;
+ SiteMapManager manager) {
+ super(new AccessibilityServiceResultCallable(context, query, manager));
}
- @Override
- public Set<? extends SearchResult> loadInBackground() {
- final Set<SearchResult> results = new HashSet<>();
- final Context context = getContext();
- final List<AccessibilityServiceInfo> services = mAccessibilityManager
- .getInstalledAccessibilityServiceList();
- final IconDrawableFactory iconFactory = IconDrawableFactory.newInstance(mContext);
- final String screenTitle = context.getString(R.string.accessibility_settings);
- for (AccessibilityServiceInfo service : services) {
- if (service == null) {
- continue;
- }
- final ResolveInfo resolveInfo = service.getResolveInfo();
- if (service.getResolveInfo() == null) {
- continue;
- }
- final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
- final CharSequence title = resolveInfo.loadLabel(mPackageManager);
- final int wordDiff = getWordDifference(title.toString(), mQuery);
- if (wordDiff == NAME_NO_MATCH) {
- continue;
- }
- final Drawable icon;
- if (resolveInfo.getIconResource() == 0) {
- icon = ContextCompat.getDrawable(context, R.mipmap.ic_accessibility_generic);
- } else {
- icon = iconFactory.getBadgedIcon(
- resolveInfo.serviceInfo,
- resolveInfo.serviceInfo.applicationInfo,
- mUserId);
- }
- final String componentName = new ComponentName(serviceInfo.packageName,
- serviceInfo.name).flattenToString();
- final Intent intent = DatabaseIndexingUtils.buildSearchResultPageIntent(context,
- AccessibilitySettings.class.getName(), componentName, screenTitle);
+ static class AccessibilityServiceResultCallable implements
+ Callable<List<? extends SearchResult>> {
- results.add(new SearchResult.Builder()
- .setTitle(title)
- .addBreadcrumbs(getBreadCrumb())
- .setPayload(new ResultPayload(intent))
- .setRank(wordDiff)
- .setIcon(icon)
- .setStableId(Objects.hash(screenTitle, componentName))
- .build());
+ private static final int NAME_NO_MATCH = -1;
+
+ private final Context mContext;
+ private List<String> mBreadcrumb;
+ private SiteMapManager mSiteMapManager;
+ @VisibleForTesting
+ final String mQuery;
+ private final AccessibilityManager mAccessibilityManager;
+ private final PackageManager mPackageManager;
+ private final int mUserId;
+
+ public AccessibilityServiceResultCallable(Context context, String query,
+ SiteMapManager mapManager) {
+ mUserId = UserHandle.myUserId();
+ mContext = context;
+ mSiteMapManager = mapManager;
+ mPackageManager = context.getPackageManager();
+ mAccessibilityManager =
+ (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ mQuery = query;
}
- return results;
- }
- private List<String> getBreadCrumb() {
- if (mBreadcrumb == null || mBreadcrumb.isEmpty()) {
- final Context context = getContext();
- mBreadcrumb = mSiteMapManager.buildBreadCrumb(
- context, AccessibilitySettings.class.getName(),
- context.getString(R.string.accessibility_settings));
+ @Override
+ public List<? extends SearchResult> call() throws Exception {
+ long startTime = System.currentTimeMillis();
+ final List<SearchResult> results = new ArrayList<>();
+ final List<AccessibilityServiceInfo> services = mAccessibilityManager
+ .getInstalledAccessibilityServiceList();
+ final IconDrawableFactory iconFactory = IconDrawableFactory.newInstance(mContext);
+ final String screenTitle = mContext.getString(R.string.accessibility_settings);
+ for (AccessibilityServiceInfo service : services) {
+ if (service == null) {
+ continue;
+ }
+ final ResolveInfo resolveInfo = service.getResolveInfo();
+ if (service.getResolveInfo() == null) {
+ continue;
+ }
+ final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+ final CharSequence title = resolveInfo.loadLabel(mPackageManager);
+ final int wordDiff = getWordDifference(title.toString(), mQuery);
+ if (wordDiff == NAME_NO_MATCH) {
+ continue;
+ }
+ final Drawable icon;
+ if (resolveInfo.getIconResource() == 0) {
+ icon = ContextCompat.getDrawable(mContext, R.mipmap.ic_accessibility_generic);
+ } else {
+ icon = iconFactory.getBadgedIcon(
+ resolveInfo.serviceInfo,
+ resolveInfo.serviceInfo.applicationInfo,
+ mUserId);
+ }
+ final String componentName = new ComponentName(serviceInfo.packageName,
+ serviceInfo.name).flattenToString();
+ final Intent intent = DatabaseIndexingUtils.buildSearchResultPageIntent(mContext,
+ AccessibilitySettings.class.getName(), componentName, screenTitle);
+
+ results.add(new SearchResult.Builder()
+ .setTitle(title)
+ .addBreadcrumbs(getBreadCrumb())
+ .setPayload(new ResultPayload(intent))
+ .setRank(wordDiff)
+ .setIcon(icon)
+ .setStableId(Objects.hash(screenTitle, componentName))
+ .build());
+ }
+ Collections.sort(results);
+ Log.i(TAG, "A11y search loading took:" + (System.currentTimeMillis() - startTime));
+ return results;
}
- return mBreadcrumb;
- }
- @Override
- protected void onDiscardResult(Set<? extends SearchResult> result) {
-
+ private List<String> getBreadCrumb() {
+ if (mBreadcrumb == null || mBreadcrumb.isEmpty()) {
+ mBreadcrumb = mSiteMapManager.buildBreadCrumb(
+ mContext, AccessibilitySettings.class.getName(),
+ mContext.getString(R.string.accessibility_settings));
+ }
+ return mBreadcrumb;
+ }
}
}
diff --git a/src/com/android/settings/search/CursorToSearchResultConverter.java b/src/com/android/settings/search/CursorToSearchResultConverter.java
index ce64de9..8528c56 100644
--- a/src/com/android/settings/search/CursorToSearchResultConverter.java
+++ b/src/com/android/settings/search/CursorToSearchResultConverter.java
@@ -36,16 +36,6 @@
import java.util.Set;
import static com.android.settings.search.DatabaseResultLoader.BASE_RANKS;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_CLASS_NAME;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_ICON;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_ID;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_KEY;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_PAYLOAD;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_PAYLOAD_TYPE;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_SCREEN_TITLE;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_SUMMARY_ON;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_TITLE;
import static com.android.settings.search.SearchResult.TOP_RANK;
/**
@@ -62,6 +52,25 @@
private static final String TAG = "CursorConverter";
+ /**
+ * These indices are used to match the columns of the this loader's SELECT statement.
+ * These are not necessarily the same order nor similar coverage as the schema defined in
+ * IndexDatabaseHelper
+ */
+ public static final int COLUMN_INDEX_ID = 0;
+ public static final int COLUMN_INDEX_TITLE = 1;
+ public static final int COLUMN_INDEX_SUMMARY_ON = 2;
+ public static final int COLUMN_INDEX_SUMMARY_OFF = 3;
+ public static final int COLUMN_INDEX_CLASS_NAME = 4;
+ public static final int COLUMN_INDEX_SCREEN_TITLE = 5;
+ public static final int COLUMN_INDEX_ICON = 6;
+ public static final int COLUMN_INDEX_INTENT_ACTION = 7;
+ public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 8;
+ public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 9;
+ public static final int COLUMN_INDEX_KEY = 10;
+ public static final int COLUMN_INDEX_PAYLOAD_TYPE = 11;
+ public static final int COLUMN_INDEX_PAYLOAD = 12;
+
private final Context mContext;
private final int LONG_TITLE_LENGTH = 20;
diff --git a/src/com/android/settings/search/DatabaseIndexingManager.java b/src/com/android/settings/search/DatabaseIndexingManager.java
index e94befb..970b50f 100644
--- a/src/com/android/settings/search/DatabaseIndexingManager.java
+++ b/src/com/android/settings/search/DatabaseIndexingManager.java
@@ -17,11 +17,13 @@
package com.android.settings.search;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_ID;
-import static com.android.settings.search.DatabaseResultLoader
+
+import static com.android.settings.search.CursorToSearchResultConverter.COLUMN_INDEX_ID;
+import static com.android.settings.search.CursorToSearchResultConverter
.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE;
-import static com.android.settings.search.DatabaseResultLoader.COLUMN_INDEX_KEY;
+import static com.android.settings.search.CursorToSearchResultConverter.COLUMN_INDEX_KEY;
import static com.android.settings.search.DatabaseResultLoader.SELECT_COLUMNS;
+import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DOCID;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.CLASS_NAME;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_ENTRIES;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_KEYWORDS;
@@ -31,7 +33,6 @@
.DATA_SUMMARY_ON_NORMALIZED;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_TITLE;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DATA_TITLE_NORMALIZED;
-import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.DOCID;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.ENABLED;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.ICON;
import static com.android.settings.search.IndexDatabaseHelper.IndexColumns.INTENT_ACTION;
diff --git a/src/com/android/settings/search/DatabaseResultLoader.java b/src/com/android/settings/search/DatabaseResultLoader.java
index c1663ab..66548a4 100644
--- a/src/com/android/settings/search/DatabaseResultLoader.java
+++ b/src/com/android/settings/search/DatabaseResultLoader.java
@@ -24,35 +24,31 @@
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+import android.util.Pair;
import com.android.settings.dashboard.SiteMapManager;
-import com.android.settings.utils.AsyncLoader;
+import com.android.settings.overlay.FeatureFactory;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
- * AsyncTask to retrieve Settings, First party app and any intent based results.
+ * AsyncTask to retrieve Settings, first party app and any intent based results.
*/
-public class DatabaseResultLoader extends AsyncLoader<Set<? extends SearchResult>> {
- private static final String LOG = "DatabaseResultLoader";
+public class DatabaseResultLoader extends FutureTask<List<? extends SearchResult>> {
- /* These indices are used to match the columns of the this loader's SELECT statement.
- These are not necessarily the same order nor similar coverage as the schema defined in
- IndexDatabaseHelper */
- public static final int COLUMN_INDEX_ID = 0;
- public static final int COLUMN_INDEX_TITLE = 1;
- public static final int COLUMN_INDEX_SUMMARY_ON = 2;
- public static final int COLUMN_INDEX_SUMMARY_OFF = 3;
- public static final int COLUMN_INDEX_CLASS_NAME = 4;
- public static final int COLUMN_INDEX_SCREEN_TITLE = 5;
- public static final int COLUMN_INDEX_ICON = 6;
- public static final int COLUMN_INDEX_INTENT_ACTION = 7;
- public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE = 8;
- public static final int COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS = 9;
- public static final int COLUMN_INDEX_KEY = 10;
- public static final int COLUMN_INDEX_PAYLOAD_TYPE = 11;
- public static final int COLUMN_INDEX_PAYLOAD = 12;
+ private static final String TAG = "DatabaseResultLoader";
public static final String[] SELECT_COLUMNS = {
IndexColumns.DOCID,
@@ -82,194 +78,267 @@
IndexColumns.DATA_SUMMARY_OFF_NORMALIZED,
};
- public static final String[] MATCH_COLUMNS_TERTIARY = {
- IndexColumns.DATA_KEYWORDS,
- IndexColumns.DATA_ENTRIES
- };
-
/**
* Base ranks defines the best possible rank based on what the query matches.
- * If the query matches the prefix of the first word in the title, the best rank it can be is 1
- * If the query matches the prefix of the other words in the title, the best rank it can be is 3
+ * If the query matches the prefix of the first word in the title, the best rank it can be
+ * is 1
+ * If the query matches the prefix of the other words in the title, the best rank it can be
+ * is 3
* If the query only matches the summary, the best rank it can be is 7
* If the query only matches keywords or entries, the best rank it can be is 9
*/
public static final int[] BASE_RANKS = {1, 3, 7, 9};
- @VisibleForTesting
- final String mQueryText;
- private final Context mContext;
- private final CursorToSearchResultConverter mConverter;
- private final SiteMapManager mSiteMapManager;
-
- public DatabaseResultLoader(Context context, String queryText, SiteMapManager mapManager) {
- super(context);
- mSiteMapManager = mapManager;
- mContext = context;
- mQueryText = queryText;
- mConverter = new CursorToSearchResultConverter(context);
+ public DatabaseResultLoader(Context context, String query, SiteMapManager manager) {
+ super(new StaticSearchResultCallable(context, query, manager));
}
- @Override
- protected void onDiscardResult(Set<? extends SearchResult> result) {
- // TODO Search
- }
+ static class StaticSearchResultCallable implements
+ Callable<List<? extends SearchResult>> {
- @Override
- public Set<? extends SearchResult> loadInBackground() {
- if (mQueryText == null || mQueryText.isEmpty()) {
- return null;
+ public final String[] MATCH_COLUMNS_TERTIARY = {
+ IndexColumns.DATA_KEYWORDS,
+ IndexColumns.DATA_ENTRIES
+ };
+
+ @VisibleForTesting
+ final String mQueryText;
+ private final Context mContext;
+ private final CursorToSearchResultConverter mConverter;
+ private final SiteMapManager mSiteMapManager;
+ private final SearchFeatureProvider mFeatureProvider;
+
+ public StaticSearchResultCallable(Context context, String queryText,
+ SiteMapManager mapManager) {
+ mContext = context;
+ mSiteMapManager = mapManager;
+ mQueryText = queryText;
+ mConverter = new CursorToSearchResultConverter(context);
+ mFeatureProvider = FeatureFactory.getFactory(context).getSearchFeatureProvider();
}
- final Set<SearchResult> results = new HashSet<>();
+ @Override
+ public List<? extends SearchResult> call() {
+ if (mQueryText == null || mQueryText.isEmpty()) {
+ return new ArrayList<>();
+ }
- results.addAll(firstWordQuery(MATCH_COLUMNS_PRIMARY, BASE_RANKS[0]));
- results.addAll(secondaryWordQuery(MATCH_COLUMNS_PRIMARY, BASE_RANKS[1]));
- results.addAll(anyWordQuery(MATCH_COLUMNS_SECONDARY, BASE_RANKS[2]));
- results.addAll(anyWordQuery(MATCH_COLUMNS_TERTIARY, BASE_RANKS[3]));
- return results;
- }
+ // TODO (b/68656233) Consolidate timing metrics
+ long startTime = System.currentTimeMillis();
+ // Start a Future to get search result scores.
+ FutureTask<List<Pair<String, Float>>> rankerTask = mFeatureProvider.getRankerTask(
+ mContext, mQueryText);
- @Override
- protected boolean onCancelLoad() {
- // TODO
- return super.onCancelLoad();
- }
+ if (rankerTask != null) {
+ ExecutorService executorService = mFeatureProvider.getExecutorService();
+ executorService.execute(rankerTask);
+ }
- /**
- * Creates and executes the query which matches prefixes of the first word of the given columns.
- *
- * @param matchColumns The columns to match on
- * @param baseRank The highest rank achievable by these results
- * @return A set of the matching results.
- */
- private Set<SearchResult> firstWordQuery(String[] matchColumns, int baseRank) {
- final String whereClause = buildSingleWordWhereClause(matchColumns);
- final String query = mQueryText + "%";
- final String[] selection = buildSingleWordSelection(query, matchColumns.length);
+ final Set<SearchResult> resultSet = new HashSet<>();
- return query(whereClause, selection, baseRank);
- }
+ resultSet.addAll(firstWordQuery(MATCH_COLUMNS_PRIMARY, BASE_RANKS[0]));
+ resultSet.addAll(secondaryWordQuery(MATCH_COLUMNS_PRIMARY, BASE_RANKS[1]));
+ resultSet.addAll(anyWordQuery(MATCH_COLUMNS_SECONDARY, BASE_RANKS[2]));
+ resultSet.addAll(anyWordQuery(MATCH_COLUMNS_TERTIARY, BASE_RANKS[3]));
- /**
- * Creates and executes the query which matches prefixes of the non-first words of the
- * given columns.
- *
- * @param matchColumns The columns to match on
- * @param baseRank The highest rank achievable by these results
- * @return A set of the matching results.
- */
- private Set<SearchResult> secondaryWordQuery(String[] matchColumns, int baseRank) {
- final String whereClause = buildSingleWordWhereClause(matchColumns);
- final String query = "% " + mQueryText + "%";
- final String[] selection = buildSingleWordSelection(query, matchColumns.length);
+ // Try to retrieve the scores in time. Otherwise use static ranking.
+ if (rankerTask != null) {
+ try {
+ final long timeoutMs = mFeatureProvider.smartSearchRankingTimeoutMs(mContext);
+ List<Pair<String, Float>> searchRankScores = rankerTask.get(timeoutMs,
+ TimeUnit.MILLISECONDS);
+ return getDynamicRankedResults(resultSet, searchRankScores);
+ } catch (TimeoutException | InterruptedException | ExecutionException e) {
+ Log.d(TAG, "Error waiting for result scores: " + e);
+ }
+ }
- return query(whereClause, selection, baseRank);
- }
-
- /**
- * Creates and executes the query which matches prefixes of the any word of the given columns.
- *
- * @param matchColumns The columns to match on
- * @param baseRank The highest rank achievable by these results
- * @return A set of the matching results.
- */
- private Set<SearchResult> anyWordQuery(String[] matchColumns, int baseRank) {
- final String whereClause = buildTwoWordWhereClause(matchColumns);
- final String[] selection = buildAnyWordSelection(matchColumns.length * 2);
-
- return query(whereClause, selection, baseRank);
- }
-
- /**
- * Generic method used by all of the query methods above to execute a query.
- *
- * @param whereClause Where clause for the SQL query which uses bindings.
- * @param selection List of the transformed query to match each bind in the whereClause
- * @param baseRank The highest rank achievable by these results.
- * @return A set of the matching results.
- */
- private Set<SearchResult> query(String whereClause, String[] selection, int baseRank) {
- final SQLiteDatabase database =
- IndexDatabaseHelper.getInstance(mContext).getReadableDatabase();
- try (Cursor resultCursor = database.query(TABLE_PREFS_INDEX, SELECT_COLUMNS, whereClause,
- selection, null, null, null)) {
- return mConverter.convertCursor(mSiteMapManager, resultCursor, baseRank);
+ List<SearchResult> resultList = new ArrayList<>(resultSet);
+ Collections.sort(resultList);
+ Log.i(TAG, "Static search loading took:" + (System.currentTimeMillis() - startTime));
+ return resultList;
}
- }
- /**
- * Builds the SQLite WHERE clause that matches all matchColumns for a single query.
- *
- * @param matchColumns List of columns that will be used for matching.
- * @return The constructed WHERE clause.
- */
- private static String buildSingleWordWhereClause(String[] matchColumns) {
- StringBuilder sb = new StringBuilder(" (");
- final int count = matchColumns.length;
- for (int n = 0; n < count; n++) {
- sb.append(matchColumns[n]);
- sb.append(" like ? ");
- if (n < count - 1) {
- sb.append(" OR ");
+ // TODO (b/33577327) Retrieve all search results with a single query.
+
+ /**
+ * Creates and executes the query which matches prefixes of the first word of the given
+ * columns.
+ *
+ * @param matchColumns The columns to match on
+ * @param baseRank The highest rank achievable by these results
+ * @return A set of the matching results.
+ */
+ private Set<SearchResult> firstWordQuery(String[] matchColumns, int baseRank) {
+ final String whereClause = buildSingleWordWhereClause(matchColumns);
+ final String query = mQueryText + "%";
+ final String[] selection = buildSingleWordSelection(query, matchColumns.length);
+
+ return query(whereClause, selection, baseRank);
+ }
+
+ /**
+ * Creates and executes the query which matches prefixes of the non-first words of the
+ * given columns.
+ *
+ * @param matchColumns The columns to match on
+ * @param baseRank The highest rank achievable by these results
+ * @return A set of the matching results.
+ */
+ private Set<SearchResult> secondaryWordQuery(String[] matchColumns, int baseRank) {
+ final String whereClause = buildSingleWordWhereClause(matchColumns);
+ final String query = "% " + mQueryText + "%";
+ final String[] selection = buildSingleWordSelection(query, matchColumns.length);
+
+ return query(whereClause, selection, baseRank);
+ }
+
+ /**
+ * Creates and executes the query which matches prefixes of the any word of the given
+ * columns.
+ *
+ * @param matchColumns The columns to match on
+ * @param baseRank The highest rank achievable by these results
+ * @return A set of the matching results.
+ */
+ private Set<SearchResult> anyWordQuery(String[] matchColumns, int baseRank) {
+ final String whereClause = buildTwoWordWhereClause(matchColumns);
+ final String[] selection = buildAnyWordSelection(matchColumns.length * 2);
+
+ return query(whereClause, selection, baseRank);
+ }
+
+ /**
+ * Generic method used by all of the query methods above to execute a query.
+ *
+ * @param whereClause Where clause for the SQL query which uses bindings.
+ * @param selection List of the transformed query to match each bind in the whereClause
+ * @param baseRank The highest rank achievable by these results.
+ * @return A set of the matching results.
+ */
+ private Set<SearchResult> query(String whereClause, String[] selection, int baseRank) {
+ final SQLiteDatabase database =
+ IndexDatabaseHelper.getInstance(mContext).getReadableDatabase();
+ try (Cursor resultCursor = database.query(TABLE_PREFS_INDEX, SELECT_COLUMNS,
+ whereClause,
+ selection, null, null, null)) {
+ return mConverter.convertCursor(mSiteMapManager, resultCursor, baseRank);
}
}
- sb.append(") AND enabled = 1");
- return sb.toString();
- }
- /**
- * Builds the SQLite WHERE clause that matches all matchColumns to two different queries.
- *
- * @param matchColumns List of columns that will be used for matching.
- * @return The constructed WHERE clause.
- */
- private static String buildTwoWordWhereClause(String[] matchColumns) {
- StringBuilder sb = new StringBuilder(" (");
- final int count = matchColumns.length;
- for (int n = 0; n < count; n++) {
- sb.append(matchColumns[n]);
- sb.append(" like ? OR ");
- sb.append(matchColumns[n]);
- sb.append(" like ?");
- if (n < count - 1) {
- sb.append(" OR ");
+ /**
+ * Builds the SQLite WHERE clause that matches all matchColumns for a single query.
+ *
+ * @param matchColumns List of columns that will be used for matching.
+ * @return The constructed WHERE clause.
+ */
+ private static String buildSingleWordWhereClause(String[] matchColumns) {
+ StringBuilder sb = new StringBuilder(" (");
+ final int count = matchColumns.length;
+ for (int n = 0; n < count; n++) {
+ sb.append(matchColumns[n]);
+ sb.append(" like ? ");
+ if (n < count - 1) {
+ sb.append(" OR ");
+ }
}
+ sb.append(") AND enabled = 1");
+ return sb.toString();
}
- sb.append(") AND enabled = 1");
- return sb.toString();
- }
- /**
- * Fills out the selection array to match the query as the prefix of a single word.
- *
- * @param size is the number of columns to be matched.
- */
- private String[] buildSingleWordSelection(String query, int size) {
- String[] selection = new String[size];
-
- for (int i = 0; i < size; i++) {
- selection[i] = query;
+ /**
+ * Builds the SQLite WHERE clause that matches all matchColumns to two different queries.
+ *
+ * @param matchColumns List of columns that will be used for matching.
+ * @return The constructed WHERE clause.
+ */
+ private static String buildTwoWordWhereClause(String[] matchColumns) {
+ StringBuilder sb = new StringBuilder(" (");
+ final int count = matchColumns.length;
+ for (int n = 0; n < count; n++) {
+ sb.append(matchColumns[n]);
+ sb.append(" like ? OR ");
+ sb.append(matchColumns[n]);
+ sb.append(" like ?");
+ if (n < count - 1) {
+ sb.append(" OR ");
+ }
+ }
+ sb.append(") AND enabled = 1");
+ return sb.toString();
}
- return selection;
- }
- /**
- * Fills out the selection array to match the query as the prefix of a word.
- *
- * @param size is twice the number of columns to be matched. The first match is for the prefix
- * of the first word in the column. The second match is for any subsequent word
- * prefix match.
- */
- private String[] buildAnyWordSelection(int size) {
- String[] selection = new String[size];
- final String query = mQueryText + "%";
- final String subStringQuery = "% " + mQueryText + "%";
+ /**
+ * Fills out the selection array to match the query as the prefix of a single word.
+ *
+ * @param size is the number of columns to be matched.
+ */
+ private String[] buildSingleWordSelection(String query, int size) {
+ String[] selection = new String[size];
- for (int i = 0; i < (size - 1); i += 2) {
- selection[i] = query;
- selection[i + 1] = subStringQuery;
+ for (int i = 0; i < size; i++) {
+ selection[i] = query;
+ }
+ return selection;
}
- return selection;
+
+ /**
+ * Fills out the selection array to match the query as the prefix of a word.
+ *
+ * @param size is twice the number of columns to be matched. The first match is for the
+ * prefix
+ * of the first word in the column. The second match is for any subsequent word
+ * prefix match.
+ */
+ private String[] buildAnyWordSelection(int size) {
+ String[] selection = new String[size];
+ final String query = mQueryText + "%";
+ final String subStringQuery = "% " + mQueryText + "%";
+
+ for (int i = 0; i < (size - 1); i += 2) {
+ selection[i] = query;
+ selection[i + 1] = subStringQuery;
+ }
+ return selection;
+ }
+
+ private List<SearchResult> getDynamicRankedResults(Set<SearchResult> unsortedSet,
+ List<Pair<String, Float>> searchRankScores) {
+ TreeSet<SearchResult> dbResultsSortedByScores = new TreeSet<>(
+ (o1, o2) -> {
+ float score1 = getRankingScoreByStableId(searchRankScores, o1.stableId);
+ float score2 = getRankingScoreByStableId(searchRankScores, o2.stableId);
+ if (score1 > score2) {
+ return -1;
+ } else if (score1 == score2) {
+ return 0;
+ } else {
+ return 1;
+ }
+ });
+ dbResultsSortedByScores.addAll(unsortedSet);
+
+ return new ArrayList<>(dbResultsSortedByScores);
+ }
+
+ /**
+ * Looks up ranking score for stableId
+ *
+ * @param stableId String of stableId
+ * @return the ranking score corresponding to the given stableId. If there is no score
+ * available for this stableId, -Float.MAX_VALUE is returned.
+ */
+ @VisibleForTesting
+ Float getRankingScoreByStableId(List<Pair<String, Float>> searchRankScores, int stableId) {
+ for (Pair<String, Float> rankingScore : searchRankScores) {
+ if (Integer.toString(stableId).compareTo(rankingScore.first) == 0) {
+ return rankingScore.second;
+ }
+ }
+ // If stableId not found in the list, we assign the minimum score so it will appear at
+ // the end of the list.
+ Log.w(TAG, "stableId " + stableId + " was not in the ranking scores.");
+ return -Float.MAX_VALUE;
+ }
}
}
\ No newline at end of file
diff --git a/src/com/android/settings/search/InputDeviceResultLoader.java b/src/com/android/settings/search/InputDeviceResultLoader.java
index e5e6553..598281c 100644
--- a/src/com/android/settings/search/InputDeviceResultLoader.java
+++ b/src/com/android/settings/search/InputDeviceResultLoader.java
@@ -26,6 +26,7 @@
import android.hardware.input.InputManager;
import android.hardware.input.KeyboardLayout;
import android.support.annotation.VisibleForTesting;
+import android.util.Log;
import android.view.InputDevice;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
@@ -35,20 +36,24 @@
import com.android.settings.dashboard.SiteMapManager;
import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
import com.android.settings.inputmethod.PhysicalKeyboardFragment;
-import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
/**
* Search result for input devices (physical/virtual keyboard, game controllers, etc)
*/
-public class InputDeviceResultLoader extends AsyncLoader<Set<? extends SearchResult>> {
- private static final int NAME_NO_MATCH = -1;
+
+public class InputDeviceResultLoader extends FutureTask<List<? extends SearchResult>> {
+
+ private static final String TAG = "InputResultFutureTask";
@VisibleForTesting
static final String PHYSICAL_KEYBOARD_FRAGMENT = PhysicalKeyboardFragment.class.getName();
@@ -56,145 +61,151 @@
static final String VIRTUAL_KEYBOARD_FRAGMENT =
AvailableVirtualKeyboardFragment.class.getName();
- private final SiteMapManager mSiteMapManager;
- private final InputManager mInputManager;
- private final InputMethodManager mImm;
- private final PackageManager mPackageManager;
- @VisibleForTesting
- final String mQuery;
-
- private List<String> mPhysicalKeyboardBreadcrumb;
- private List<String> mVirtualKeyboardBreadcrumb;
-
- public InputDeviceResultLoader(Context context, String query, SiteMapManager mapManager) {
- super(context);
- mQuery = query;
- mSiteMapManager = mapManager;
- mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
- mImm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
- mPackageManager = context.getPackageManager();
+ public InputDeviceResultLoader(Context context, String query, SiteMapManager manager) {
+ super(new InputDeviceResultCallable(context, query, manager));
}
- @Override
- protected void onDiscardResult(Set<? extends SearchResult> result) {
- }
+ static class InputDeviceResultCallable implements
+ Callable<List<? extends SearchResult>> {
+ private static final int NAME_NO_MATCH = -1;
- @Override
- public Set<? extends SearchResult> loadInBackground() {
- final Set<SearchResult> results = new HashSet<>();
- results.addAll(buildPhysicalKeyboardSearchResults());
- results.addAll(buildVirtualKeyboardSearchResults());
- return results;
- }
+ private final Context mContext;
+ private final SiteMapManager mSiteMapManager;
+ private final InputManager mInputManager;
+ private final InputMethodManager mImm;
+ private final PackageManager mPackageManager;
+ @VisibleForTesting
+ final String mQuery;
- private Set<SearchResult> buildPhysicalKeyboardSearchResults() {
- final Set<SearchResult> results = new HashSet<>();
- final Context context = getContext();
- final String screenTitle = context.getString(R.string.physical_keyboard_title);
+ private List<String> mPhysicalKeyboardBreadcrumb;
+ private List<String> mVirtualKeyboardBreadcrumb;
- for (final InputDevice device : getPhysicalFullKeyboards()) {
- final String deviceName = device.getName();
- final int wordDiff = InstalledAppResultLoader.getWordDifference(deviceName, mQuery);
- if (wordDiff == NAME_NO_MATCH) {
- continue;
+ public InputDeviceResultCallable(Context context, String query, SiteMapManager mapManager) {
+ mContext = context;
+ mQuery = query;
+ mSiteMapManager = mapManager;
+ mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
+ mImm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
+ mPackageManager = context.getPackageManager();
+ }
+
+ @Override
+ public List<? extends SearchResult> call() {
+ long startTime = System.currentTimeMillis();
+ final List<SearchResult> results = new ArrayList<>();
+ results.addAll(buildPhysicalKeyboardSearchResults());
+ results.addAll(buildVirtualKeyboardSearchResults());
+ Collections.sort(results);
+ Log.i(TAG, "Input search loading took:" + (System.currentTimeMillis() - startTime));
+ return results;
+ }
+
+ private Set<SearchResult> buildPhysicalKeyboardSearchResults() {
+ final Set<SearchResult> results = new HashSet<>();
+ final String screenTitle = mContext.getString(R.string.physical_keyboard_title);
+
+ for (final InputDevice device : getPhysicalFullKeyboards()) {
+ final String deviceName = device.getName();
+ final int wordDiff = InstalledAppResultLoader.getWordDifference(deviceName,
+ mQuery);
+ if (wordDiff == NAME_NO_MATCH) {
+ continue;
+ }
+ final String keyboardLayoutDescriptor = mInputManager
+ .getCurrentKeyboardLayoutForInputDevice(device.getIdentifier());
+ final KeyboardLayout keyboardLayout = (keyboardLayoutDescriptor != null)
+ ? mInputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null;
+ final String summary = (keyboardLayout != null)
+ ? keyboardLayout.toString()
+ : mContext.getString(R.string.keyboard_layout_default_label);
+
+ final Intent intent = DatabaseIndexingUtils.buildSearchResultPageIntent(mContext,
+ PHYSICAL_KEYBOARD_FRAGMENT, deviceName, screenTitle);
+ results.add(new SearchResult.Builder()
+ .setTitle(deviceName)
+ .setPayload(new ResultPayload(intent))
+ .setStableId(Objects.hash(PHYSICAL_KEYBOARD_FRAGMENT, deviceName))
+ .setSummary(summary)
+ .setRank(wordDiff)
+ .addBreadcrumbs(getPhysicalKeyboardBreadCrumb())
+ .build());
}
- final String keyboardLayoutDescriptor = mInputManager
- .getCurrentKeyboardLayoutForInputDevice(device.getIdentifier());
- final KeyboardLayout keyboardLayout = (keyboardLayoutDescriptor != null)
- ? mInputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null;
- final String summary = (keyboardLayout != null)
- ? keyboardLayout.toString()
- : context.getString(R.string.keyboard_layout_default_label);
- final String key = deviceName;
-
- final Intent intent = DatabaseIndexingUtils.buildSearchResultPageIntent(context,
- PHYSICAL_KEYBOARD_FRAGMENT, key, screenTitle);
- results.add(new SearchResult.Builder()
- .setTitle(deviceName)
- .setPayload(new ResultPayload(intent))
- .setStableId(Objects.hash(PHYSICAL_KEYBOARD_FRAGMENT, key))
- .setSummary(summary)
- .setRank(wordDiff)
- .addBreadcrumbs(getPhysicalKeyboardBreadCrumb())
- .build());
+ return results;
}
- return results;
- }
- private Set<SearchResult> buildVirtualKeyboardSearchResults() {
- final Set<SearchResult> results = new HashSet<>();
- final Context context = getContext();
- final String screenTitle = context.getString(R.string.add_virtual_keyboard);
- final List<InputMethodInfo> inputMethods = mImm.getInputMethodList();
- for (InputMethodInfo info : inputMethods) {
- final String title = info.loadLabel(mPackageManager).toString();
- final String summary = InputMethodAndSubtypeUtil
- .getSubtypeLocaleNameListAsSentence(getAllSubtypesOf(info), context, info);
- int wordDiff = InstalledAppResultLoader.getWordDifference(title, mQuery);
- if (wordDiff == NAME_NO_MATCH) {
- wordDiff = InstalledAppResultLoader.getWordDifference(summary, mQuery);
+ private Set<SearchResult> buildVirtualKeyboardSearchResults() {
+ final Set<SearchResult> results = new HashSet<>();
+ final String screenTitle = mContext.getString(R.string.add_virtual_keyboard);
+ final List<InputMethodInfo> inputMethods = mImm.getInputMethodList();
+ for (InputMethodInfo info : inputMethods) {
+ final String title = info.loadLabel(mPackageManager).toString();
+ final String summary = InputMethodAndSubtypeUtil
+ .getSubtypeLocaleNameListAsSentence(getAllSubtypesOf(info), mContext, info);
+ int wordDiff = InstalledAppResultLoader.getWordDifference(title, mQuery);
+ if (wordDiff == NAME_NO_MATCH) {
+ wordDiff = InstalledAppResultLoader.getWordDifference(summary, mQuery);
+ }
+ if (wordDiff == NAME_NO_MATCH) {
+ continue;
+ }
+ final ServiceInfo serviceInfo = info.getServiceInfo();
+ final String key = new ComponentName(serviceInfo.packageName, serviceInfo.name)
+ .flattenToString();
+ final Intent intent = DatabaseIndexingUtils.buildSearchResultPageIntent(mContext,
+ VIRTUAL_KEYBOARD_FRAGMENT, key, screenTitle);
+ results.add(new SearchResult.Builder()
+ .setTitle(title)
+ .setSummary(summary)
+ .setRank(wordDiff)
+ .setStableId(Objects.hash(VIRTUAL_KEYBOARD_FRAGMENT, key))
+ .addBreadcrumbs(getVirtualKeyboardBreadCrumb())
+ .setPayload(new ResultPayload(intent))
+ .build());
}
- if (wordDiff == NAME_NO_MATCH) {
- continue;
+ return results;
+ }
+
+ private List<String> getPhysicalKeyboardBreadCrumb() {
+ if (mPhysicalKeyboardBreadcrumb == null || mPhysicalKeyboardBreadcrumb.isEmpty()) {
+ mPhysicalKeyboardBreadcrumb = mSiteMapManager.buildBreadCrumb(
+ mContext, PHYSICAL_KEYBOARD_FRAGMENT,
+ mContext.getString(R.string.physical_keyboard_title));
}
- final ServiceInfo serviceInfo = info.getServiceInfo();
- final String key = new ComponentName(serviceInfo.packageName, serviceInfo.name)
- .flattenToString();
- final Intent intent = DatabaseIndexingUtils.buildSearchResultPageIntent(context,
- VIRTUAL_KEYBOARD_FRAGMENT, key, screenTitle);
- results.add(new SearchResult.Builder()
- .setTitle(title)
- .setSummary(summary)
- .setRank(wordDiff)
- .setStableId(Objects.hash(VIRTUAL_KEYBOARD_FRAGMENT, key))
- .addBreadcrumbs(getVirtualKeyboardBreadCrumb())
- .setPayload(new ResultPayload(intent))
- .build());
+ return mPhysicalKeyboardBreadcrumb;
}
- return results;
- }
- private List<String> getPhysicalKeyboardBreadCrumb() {
- if (mPhysicalKeyboardBreadcrumb == null || mPhysicalKeyboardBreadcrumb.isEmpty()) {
- final Context context = getContext();
- mPhysicalKeyboardBreadcrumb = mSiteMapManager.buildBreadCrumb(
- context, PHYSICAL_KEYBOARD_FRAGMENT,
- context.getString(R.string.physical_keyboard_title));
+
+ private List<String> getVirtualKeyboardBreadCrumb() {
+ if (mVirtualKeyboardBreadcrumb == null || mVirtualKeyboardBreadcrumb.isEmpty()) {
+ final Context context = mContext;
+ mVirtualKeyboardBreadcrumb = mSiteMapManager.buildBreadCrumb(
+ context, VIRTUAL_KEYBOARD_FRAGMENT,
+ context.getString(R.string.add_virtual_keyboard));
+ }
+ return mVirtualKeyboardBreadcrumb;
}
- return mPhysicalKeyboardBreadcrumb;
- }
-
- private List<String> getVirtualKeyboardBreadCrumb() {
- if (mVirtualKeyboardBreadcrumb == null || mVirtualKeyboardBreadcrumb.isEmpty()) {
- final Context context = getContext();
- mVirtualKeyboardBreadcrumb = mSiteMapManager.buildBreadCrumb(
- context, VIRTUAL_KEYBOARD_FRAGMENT,
- context.getString(R.string.add_virtual_keyboard));
- }
- return mVirtualKeyboardBreadcrumb;
- }
-
- private List<InputDevice> getPhysicalFullKeyboards() {
- final List<InputDevice> keyboards = new ArrayList<>();
- final int[] deviceIds = InputDevice.getDeviceIds();
- if (deviceIds != null) {
- for (int deviceId : deviceIds) {
- final InputDevice device = InputDevice.getDevice(deviceId);
- if (device != null && !device.isVirtual() && device.isFullKeyboard()) {
- keyboards.add(device);
+ private List<InputDevice> getPhysicalFullKeyboards() {
+ final List<InputDevice> keyboards = new ArrayList<>();
+ final int[] deviceIds = InputDevice.getDeviceIds();
+ if (deviceIds != null) {
+ for (int deviceId : deviceIds) {
+ final InputDevice device = InputDevice.getDevice(deviceId);
+ if (device != null && !device.isVirtual() && device.isFullKeyboard()) {
+ keyboards.add(device);
+ }
}
}
+ return keyboards;
}
- return keyboards;
- }
- private static List<InputMethodSubtype> getAllSubtypesOf(final InputMethodInfo imi) {
- final int subtypeCount = imi.getSubtypeCount();
- final List<InputMethodSubtype> allSubtypes = new ArrayList<>(subtypeCount);
- for (int index = 0; index < subtypeCount; index++) {
- allSubtypes.add(imi.getSubtypeAt(index));
+ private static List<InputMethodSubtype> getAllSubtypesOf(final InputMethodInfo imi) {
+ final int subtypeCount = imi.getSubtypeCount();
+ final List<InputMethodSubtype> allSubtypes = new ArrayList<>(subtypeCount);
+ for (int index = 0; index < subtypeCount; index++) {
+ allSubtypes.add(imi.getSubtypeAt(index));
+ }
+ return allSubtypes;
}
- return allSubtypes;
}
}
diff --git a/src/com/android/settings/search/InstalledAppResultLoader.java b/src/com/android/settings/search/InstalledAppResultLoader.java
index 7645c15..e5d8ac1 100644
--- a/src/com/android/settings/search/InstalledAppResultLoader.java
+++ b/src/com/android/settings/search/InstalledAppResultLoader.java
@@ -29,124 +29,39 @@
import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
+import android.util.Log;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.applications.manageapplications.ManageApplications;
import com.android.settings.dashboard.SiteMapManager;
-import com.android.settings.utils.AsyncLoader;
import com.android.settingslib.wrapper.PackageManagerWrapper;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
/**
* Search loader for installed apps.
*/
-public class InstalledAppResultLoader extends AsyncLoader<Set<? extends SearchResult>> {
+public class InstalledAppResultLoader extends FutureTask<List<? extends SearchResult>> {
+
+ private static final String TAG = "InstalledAppFutureTask";
private static final int NAME_NO_MATCH = -1;
private static final Intent LAUNCHER_PROBE = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_LAUNCHER);
- private List<String> mBreadcrumb;
- private SiteMapManager mSiteMapManager;
- @VisibleForTesting
- final String mQuery;
- private final UserManager mUserManager;
- private final PackageManagerWrapper mPackageManager;
- private final List<ResolveInfo> mHomeActivities = new ArrayList<>();
-
- public InstalledAppResultLoader(Context context, PackageManagerWrapper pmWrapper,
- String query, SiteMapManager mapManager) {
- super(context);
- mSiteMapManager = mapManager;
- mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- mPackageManager = pmWrapper;
- mQuery = query;
- }
-
- @Override
- public Set<? extends SearchResult> loadInBackground() {
- final Set<AppSearchResult> results = new HashSet<>();
- final PackageManager pm = mPackageManager.getPackageManager();
-
- mHomeActivities.clear();
- mPackageManager.getHomeActivities(mHomeActivities);
-
- for (UserInfo user : getUsersToCount()) {
- final List<ApplicationInfo> apps =
- mPackageManager.getInstalledApplicationsAsUser(
- PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
- | (user.isAdmin() ? PackageManager.MATCH_ANY_USER : 0),
- user.id);
- for (ApplicationInfo info : apps) {
- if (!shouldIncludeAsCandidate(info, user)) {
- continue;
- }
- final CharSequence label = info.loadLabel(pm);
- final int wordDiff = getWordDifference(label.toString(), mQuery);
- if (wordDiff == NAME_NO_MATCH) {
- continue;
- }
- final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
- .setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
- .setData(Uri.fromParts("package", info.packageName, null))
- .putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY,
- MetricsProto.MetricsEvent.DASHBOARD_SEARCH_RESULTS);
-
- final AppSearchResult.Builder builder = new AppSearchResult.Builder();
- builder.setAppInfo(info)
- .setStableId(Objects.hash(info.packageName, user.id))
- .setTitle(info.loadLabel(pm))
- .setRank(getRank(wordDiff))
- .addBreadcrumbs(getBreadCrumb())
- .setPayload(new ResultPayload(intent));
- results.add(builder.build());
- }
- }
- return results;
- }
-
- /**
- * Returns true if the candidate should be included in candidate list
- * <p/>
- * This method matches logic in {@code ApplicationState#FILTER_DOWNLOADED_AND_LAUNCHER}.
- */
- private boolean shouldIncludeAsCandidate(ApplicationInfo info, UserInfo user) {
- // Not system app
- if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
- || (info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- return true;
- }
- // Shows up in launcher
- final Intent launchIntent = new Intent(LAUNCHER_PROBE)
- .setPackage(info.packageName);
- final List<ResolveInfo> intents = mPackageManager.queryIntentActivitiesAsUser(
- launchIntent,
- PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- user.id);
- if (intents != null && intents.size() != 0) {
- return true;
- }
- // Is launcher app itself
- return isPackageInList(mHomeActivities, info.packageName);
- }
-
- @Override
- protected void onDiscardResult(Set<? extends SearchResult> result) {
-
- }
-
- private List<UserInfo> getUsersToCount() {
- return mUserManager.getProfiles(UserHandle.myUserId());
+ public InstalledAppResultLoader(Context context, PackageManagerWrapper wrapper,
+ String query, SiteMapManager manager) {
+ super(new InstalledAppResultCallable(context, wrapper, query, manager));
}
/**
@@ -213,35 +128,133 @@
return NAME_NO_MATCH;
}
- private boolean isPackageInList(List<ResolveInfo> resolveInfos, String pkg) {
- for (ResolveInfo info : resolveInfos) {
- if (TextUtils.equals(info.activityInfo.packageName, pkg)) {
+ static class InstalledAppResultCallable implements
+ Callable<List<? extends SearchResult>> {
+
+ private final Context mContext;
+ private List<String> mBreadcrumb;
+ private SiteMapManager mSiteMapManager;
+ @VisibleForTesting
+ final String mQuery;
+ private final UserManager mUserManager;
+ private final PackageManagerWrapper mPackageManager;
+ private final List<ResolveInfo> mHomeActivities = new ArrayList<>();
+
+ public InstalledAppResultCallable(Context context, PackageManagerWrapper pmWrapper,
+ String query, SiteMapManager mapManager) {
+ mContext = context;
+ mSiteMapManager = mapManager;
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mPackageManager = pmWrapper;
+ mQuery = query;
+ }
+
+ @Override
+ public List<? extends SearchResult> call() throws Exception {
+ long startTime = System.currentTimeMillis();
+ final List<AppSearchResult> results = new ArrayList<>();
+ final PackageManager pm = mPackageManager.getPackageManager();
+
+ mHomeActivities.clear();
+ mPackageManager.getHomeActivities(mHomeActivities);
+
+ for (UserInfo user : getUsersToCount()) {
+ final List<ApplicationInfo> apps =
+ mPackageManager.getInstalledApplicationsAsUser(
+ PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+ | (user.isAdmin() ? PackageManager.MATCH_ANY_USER : 0),
+ user.id);
+ for (ApplicationInfo info : apps) {
+ if (!shouldIncludeAsCandidate(info, user)) {
+ continue;
+ }
+ final CharSequence label = info.loadLabel(pm);
+ final int wordDiff = getWordDifference(label.toString(), mQuery);
+ if (wordDiff == NAME_NO_MATCH) {
+ continue;
+ }
+ final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+ .setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+ .setData(Uri.fromParts("package", info.packageName, null))
+ .putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY,
+ MetricsProto.MetricsEvent.DASHBOARD_SEARCH_RESULTS);
+
+ final AppSearchResult.Builder builder = new AppSearchResult.Builder();
+ builder.setAppInfo(info)
+ .setStableId(Objects.hash(info.packageName, user.id))
+ .setTitle(info.loadLabel(pm))
+ .setRank(getRank(wordDiff))
+ .addBreadcrumbs(getBreadCrumb())
+ .setPayload(new ResultPayload(intent));
+ results.add(builder.build());
+ }
+ }
+ Collections.sort(results);
+ Log.i(TAG, "App search loading took:" + (System.currentTimeMillis() - startTime));
+ return results;
+ }
+
+ /**
+ * Returns true if the candidate should be included in candidate list
+ * <p/>
+ * This method matches logic in {@code ApplicationState#FILTER_DOWNLOADED_AND_LAUNCHER}.
+ */
+ private boolean shouldIncludeAsCandidate(ApplicationInfo info, UserInfo user) {
+ // Not system app
+ if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
+ || (info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
return true;
}
+ // Shows up in launcher
+ final Intent launchIntent = new Intent(LAUNCHER_PROBE)
+ .setPackage(info.packageName);
+ final List<ResolveInfo> intents = mPackageManager.queryIntentActivitiesAsUser(
+ launchIntent,
+ PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+ user.id);
+ if (intents != null && intents.size() != 0) {
+ return true;
+ }
+ // Is launcher app itself
+ return isPackageInList(mHomeActivities, info.packageName);
}
- return false;
- }
- private List<String> getBreadCrumb() {
- if (mBreadcrumb == null || mBreadcrumb.isEmpty()) {
- final Context context = getContext();
- mBreadcrumb = mSiteMapManager.buildBreadCrumb(
- context, ManageApplications.class.getName(),
- context.getString(R.string.applications_settings));
+ private List<UserInfo> getUsersToCount() {
+ return mUserManager.getProfiles(UserHandle.myUserId());
}
- return mBreadcrumb;
- }
- /**
- * A temporary ranking scheme for installed apps.
- *
- * @param wordDiff difference between query length and app name length.
- * @return the ranking.
- */
- private int getRank(int wordDiff) {
- if (wordDiff < 6) {
- return 2;
+ private boolean isPackageInList(List<ResolveInfo> resolveInfos, String pkg) {
+ for (ResolveInfo info : resolveInfos) {
+ if (TextUtils.equals(info.activityInfo.packageName, pkg)) {
+ return true;
+ }
+ }
+ return false;
}
- return 3;
+
+ private List<String> getBreadCrumb() {
+ if (mBreadcrumb == null || mBreadcrumb.isEmpty()) {
+ mBreadcrumb = mSiteMapManager.buildBreadCrumb(
+ mContext, ManageApplications.class.getName(),
+ mContext.getString(R.string.applications_settings));
+ }
+ return mBreadcrumb;
+ }
+
+ /**
+ * A temporary ranking scheme for installed apps.
+ *
+ * @param wordDiff difference between query length and app name length.
+ * @return the ranking.
+ */
+ private int getRank(int wordDiff) {
+ if (wordDiff < 6) {
+ return 2;
+ }
+ return 3;
+ }
}
}
diff --git a/src/com/android/settings/search/SearchFeatureProvider.java b/src/com/android/settings/search/SearchFeatureProvider.java
index 4df8203..42afee9 100644
--- a/src/com/android/settings/search/SearchFeatureProvider.java
+++ b/src/com/android/settings/search/SearchFeatureProvider.java
@@ -19,10 +19,14 @@
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
+import android.util.Pair;
import android.view.View;
import com.android.settings.dashboard.SiteMapManager;
-import com.android.settings.search.ranking.SearchResultsRankerCallback;
+
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.FutureTask;
/**
* FeatureProvider for Settings Search
@@ -44,25 +48,30 @@
throws SecurityException, IllegalArgumentException;
/**
+ * Returns a new loader to get settings search results.
+ */
+ SearchResultLoader getSearchResultLoader(Context context, String query);
+
+ /**
* Returns a new loader to search in index database.
*/
- DatabaseResultLoader getDatabaseSearchLoader(Context context, String query);
+ DatabaseResultLoader getStaticSearchResultTask(Context context, String query);
/**
* Returns a new loader to search installed apps.
*/
- InstalledAppResultLoader getInstalledAppSearchLoader(Context context, String query);
+ InstalledAppResultLoader getInstalledAppSearchTask(Context context, String query);
/**
* Returns a new loader to search accessibility services.
*/
- AccessibilityServiceResultLoader getAccessibilityServiceResultLoader(Context context,
+ AccessibilityServiceResultLoader getAccessibilityServiceResultTask(Context context,
String query);
/**
* Returns a new loader to search input devices.
*/
- InputDeviceResultLoader getInputDeviceResultLoader(Context context, String query);
+ InputDeviceResultLoader getInputDeviceResultTask(Context context, String query);
/**
* Returns a new loader to get all recently saved queries search terms.
@@ -96,6 +105,11 @@
boolean isIndexingComplete(Context context);
/**
+ * @return a {@link ExecutorService} to be shared between search tasks.
+ */
+ ExecutorService getExecutorService();
+
+ /**
* Initializes the feedback button in case it was dismissed.
*/
default void initFeedbackButton() {
@@ -115,23 +129,6 @@
}
/**
- * Query search results based on the input query.
- *
- * @param context application context
- * @param query input user query
- * @param searchResultsRankerCallback {@link SearchResultsRankerCallback}
- */
- default void querySearchResults(Context context, String query,
- SearchResultsRankerCallback searchResultsRankerCallback) {
- }
-
- /**
- * Cancel pending search query
- */
- default void cancelPendingSearchQuery(Context context) {
- }
-
- /**
* Notify that a search result is clicked.
*
* @param context application context
@@ -161,4 +158,10 @@
default void searchRankingWarmup(Context context) {
}
+ /**
+ * Return a FutureTask to get a list of scores for search results.
+ */
+ default FutureTask<List<Pair<String, Float>>> getRankerTask(Context context, String query) {
+ return null;
+ }
}
diff --git a/src/com/android/settings/search/SearchFeatureProviderImpl.java b/src/com/android/settings/search/SearchFeatureProviderImpl.java
index af7f177..e0fbfd7 100644
--- a/src/com/android/settings/search/SearchFeatureProviderImpl.java
+++ b/src/com/android/settings/search/SearchFeatureProviderImpl.java
@@ -22,12 +22,15 @@
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.dashboard.SiteMapManager;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.indexing.IndexData;
import com.android.settingslib.wrapper.PackageManagerWrapper;
import java.util.Locale;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
/**
* FeatureProvider for the refactored search code.
@@ -40,6 +43,7 @@
private DatabaseIndexingManager mDatabaseIndexingManager;
private SiteMapManager mSiteMapManager;
+ private ExecutorService mExecutorService;
@Override
public boolean isEnabled(Context context) {
@@ -59,26 +63,31 @@
}
@Override
- public DatabaseResultLoader getDatabaseSearchLoader(Context context, String query) {
+ public SearchResultLoader getSearchResultLoader(Context context, String query) {
+ return new SearchResultLoader(context, cleanQuery(query));
+ }
+
+ @Override
+ public DatabaseResultLoader getStaticSearchResultTask(Context context, String query) {
return new DatabaseResultLoader(context, cleanQuery(query), getSiteMapManager());
}
@Override
- public InstalledAppResultLoader getInstalledAppSearchLoader(Context context, String query) {
+ public InstalledAppResultLoader getInstalledAppSearchTask(Context context, String query) {
return new InstalledAppResultLoader(
context, new PackageManagerWrapper(context.getPackageManager()),
cleanQuery(query), getSiteMapManager());
}
@Override
- public AccessibilityServiceResultLoader getAccessibilityServiceResultLoader(Context context,
+ public AccessibilityServiceResultLoader getAccessibilityServiceResultTask(Context context,
String query) {
return new AccessibilityServiceResultLoader(context, cleanQuery(query),
getSiteMapManager());
}
@Override
- public InputDeviceResultLoader getInputDeviceResultLoader(Context context, String query) {
+ public InputDeviceResultLoader getInputDeviceResultTask(Context context, String query) {
return new InputDeviceResultLoader(context, cleanQuery(query), getSiteMapManager());
}
@@ -124,12 +133,21 @@
.histogram(context, METRICS_ACTION_SETTINGS_INDEX, indexingTime);
}
+ @Override
+ public ExecutorService getExecutorService() {
+ if (mExecutorService == null) {
+ mExecutorService = Executors.newCachedThreadPool();
+ }
+ return mExecutorService;
+ }
+
/**
* A generic method to make the query suitable for searching the database.
*
* @return the cleaned query string
*/
- private String cleanQuery(String query) {
+ @VisibleForTesting
+ String cleanQuery(String query) {
if (TextUtils.isEmpty(query)) {
return null;
}
diff --git a/src/com/android/settings/search/SearchFragment.java b/src/com/android/settings/search/SearchFragment.java
index ca951c6..e6316a8 100644
--- a/src/com/android/settings/search/SearchFragment.java
+++ b/src/com/android/settings/search/SearchFragment.java
@@ -54,8 +54,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* This fragment manages the lifecycle of indexing and searching.
@@ -68,7 +66,7 @@
* the query if the user has entered text.
*/
public class SearchFragment extends InstrumentedFragment implements SearchView.OnQueryTextListener,
- LoaderManager.LoaderCallbacks<Set<? extends SearchResult>>, IndexingCallback {
+ LoaderManager.LoaderCallbacks<List<? extends SearchResult>>, IndexingCallback {
private static final String TAG = "SearchFragment";
// State values
@@ -78,23 +76,14 @@
static final class SearchLoaderId {
// Search Query IDs
- public static final int DATABASE = 1;
- public static final int INSTALLED_APPS = 2;
- public static final int ACCESSIBILITY_SERVICES = 3;
- public static final int INPUT_DEVICES = 4;
+ public static final int SEARCH_RESULT = 1;
// Saved Query IDs
- public static final int SAVE_QUERY_TASK = 5;
- public static final int REMOVE_QUERY_TASK = 6;
- public static final int SAVED_QUERIES = 7;
+ public static final int SAVE_QUERY_TASK = 2;
+ public static final int REMOVE_QUERY_TASK = 3;
+ public static final int SAVED_QUERIES = 4;
}
-
- private static final int NUM_QUERY_LOADERS = 4;
-
- @VisibleForTesting
- AtomicInteger mUnfinishedLoadersCount = new AtomicInteger(NUM_QUERY_LOADERS);
-
@VisibleForTesting
String mQuery;
@@ -147,7 +136,7 @@
setHasOptionsMenu(true);
final LoaderManager loaderManager = getLoaderManager();
- mSearchAdapter = new SearchResultsAdapter(this, mSearchFeatureProvider);
+ mSearchAdapter = new SearchResultsAdapter(this /* fragment */);
mSavedQueryController = new SavedQueryController(
getContext(), loaderManager, mSearchAdapter);
mSearchFeatureProvider.initFeedbackButton();
@@ -277,15 +266,11 @@
if (isEmptyQuery) {
final LoaderManager loaderManager = getLoaderManager();
- loaderManager.destroyLoader(SearchLoaderId.DATABASE);
- loaderManager.destroyLoader(SearchLoaderId.INSTALLED_APPS);
- loaderManager.destroyLoader(SearchLoaderId.ACCESSIBILITY_SERVICES);
- loaderManager.destroyLoader(SearchLoaderId.INPUT_DEVICES);
+ loaderManager.destroyLoader(SearchLoaderId.SEARCH_RESULT);
mShowingSavedQuery = true;
mSavedQueryController.loadSavedQueries();
mSearchFeatureProvider.hideFeedbackButton();
} else {
- mSearchAdapter.initializeSearch(mQuery);
restartLoaders();
}
@@ -301,35 +286,25 @@
}
@Override
- public Loader<Set<? extends SearchResult>> onCreateLoader(int id, Bundle args) {
+ public Loader<List<? extends SearchResult>> onCreateLoader(int id, Bundle args) {
final Activity activity = getActivity();
- switch (id) {
- case SearchLoaderId.DATABASE:
- return mSearchFeatureProvider.getDatabaseSearchLoader(activity, mQuery);
- case SearchLoaderId.INSTALLED_APPS:
- return mSearchFeatureProvider.getInstalledAppSearchLoader(activity, mQuery);
- case SearchLoaderId.ACCESSIBILITY_SERVICES:
- return mSearchFeatureProvider.getAccessibilityServiceResultLoader(activity, mQuery);
- case SearchLoaderId.INPUT_DEVICES:
- return mSearchFeatureProvider.getInputDeviceResultLoader(activity, mQuery);
+ switch(id) {
+ case SearchLoaderId.SEARCH_RESULT:
+ return mSearchFeatureProvider.getSearchResultLoader(activity, mQuery);
default:
return null;
}
}
@Override
- public void onLoadFinished(Loader<Set<? extends SearchResult>> loader,
- Set<? extends SearchResult> data) {
- mSearchAdapter.addSearchResults(data, loader.getClass().getName());
- if (mUnfinishedLoadersCount.decrementAndGet() != 0) {
- return;
- }
- mSearchAdapter.notifyResultsLoaded();
+ public void onLoadFinished(Loader<List<? extends SearchResult>> loader,
+ List<? extends SearchResult> data) {
+ mSearchAdapter.postSearchResults(data);
}
@Override
- public void onLoaderReset(Loader<Set<? extends SearchResult>> loader) {
+ public void onLoaderReset(Loader<List<? extends SearchResult>> loader) {
}
/**
@@ -344,13 +319,8 @@
mSavedQueryController.loadSavedQueries();
} else {
final LoaderManager loaderManager = getLoaderManager();
- loaderManager.initLoader(SearchLoaderId.DATABASE, null /* args */, this /* callback */);
- loaderManager.initLoader(
- SearchLoaderId.INSTALLED_APPS, null /* args */, this /* callback */);
- loaderManager.initLoader(
- SearchLoaderId.ACCESSIBILITY_SERVICES, null /* args */, this /* callback */);
- loaderManager.initLoader(
- SearchLoaderId.INPUT_DEVICES, null /* args */, this /* callback */);
+ loaderManager.initLoader(SearchLoaderId.SEARCH_RESULT, null /* args */,
+ this /* callback */);
}
requery();
@@ -388,15 +358,8 @@
private void restartLoaders() {
mShowingSavedQuery = false;
final LoaderManager loaderManager = getLoaderManager();
- mUnfinishedLoadersCount.set(NUM_QUERY_LOADERS);
loaderManager.restartLoader(
- SearchLoaderId.DATABASE, null /* args */, this /* callback */);
- loaderManager.restartLoader(
- SearchLoaderId.INSTALLED_APPS, null /* args */, this /* callback */);
- loaderManager.restartLoader(
- SearchLoaderId.ACCESSIBILITY_SERVICES, null /* args */, this /* callback */);
- loaderManager.restartLoader(
- SearchLoaderId.INPUT_DEVICES, null /* args */, this /* callback */);
+ SearchLoaderId.SEARCH_RESULT, null /* args */, this /* callback */);
}
public String getQuery() {
@@ -453,9 +416,7 @@
taggedData.add(Pair.create(
MetricsEvent.FIELD_SETTINGS_SEARCH_RESULT_RANK,
resultViewHolder.getAdapterPosition()));
- taggedData.add(Pair.create(
- MetricsEvent.FIELD_SETTINGS_SEARCH_RESULT_ASYNC_RANKING_STATE,
- mSearchAdapter.getAsyncRankingState()));
+ // TODO (b/67744820) Move metrics to SettingsIntelligence (including ranking state).
taggedData.add(Pair.create(
MetricsEvent.FIELD_SETTINGS_SEARCH_QUERY_LENGTH,
TextUtils.isEmpty(mQuery) ? 0 : mQuery.length()));
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index cb39970..69b2f9f 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -23,7 +23,6 @@
import com.android.settings.DateTimeSettings;
import com.android.settings.DeviceInfoSettings;
import com.android.settings.DisplaySettings;
-import com.android.settings.EncryptionAndCredential;
import com.android.settings.LegalSettings;
import com.android.settings.ScreenPinningSettings;
import com.android.settings.SecuritySettings;
@@ -75,6 +74,7 @@
import com.android.settings.notification.ZenModeBehaviorSettings;
import com.android.settings.notification.ZenModeSettings;
import com.android.settings.print.PrintSettingsFragment;
+import com.android.settings.security.EncryptionAndCredential;
import com.android.settings.security.LockscreenDashboardFragment;
import com.android.settings.sim.SimSettings;
import com.android.settings.support.SupportDashboardActivity;
diff --git a/src/com/android/settings/search/SearchResultAggregator.java b/src/com/android/settings/search/SearchResultAggregator.java
new file mode 100644
index 0000000..890e3f0
--- /dev/null
+++ b/src/com/android/settings/search/SearchResultAggregator.java
@@ -0,0 +1,177 @@
+package com.android.settings.search;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.settings.overlay.FeatureFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Collects the sorted list of all setting search results.
+ *
+ * TODO (b/64939692) Convert the timing logs to metrics
+ */
+public class SearchResultAggregator {
+
+ private static final String TAG = "SearchResultAggregator";
+
+ /**
+ * Timeout for first task. Allows for longer delay.
+ */
+ private static final long LONG_CHECK_TASK_TIMEOUT_MS = 500;
+
+ /**
+ * Timeout for subsequent tasks to allow for fast returning tasks.
+ */
+ private static final long SHORT_CHECK_TASK_TIMEOUT_MS = 150;
+
+ private static SearchResultAggregator sResultAggregator;
+
+ // TODO (b/33577327) Merge the other loaders into a single dynamic loader
+ static final class ResultLoaderId {
+ static final int STATIC_RESULTS = 1;
+ static final int INSTALLED_RESULTS = 2;
+ static final int INPUT_RESULTS = 3;
+ static final int ACCESSIBILITY_RESULTS = 4;
+ }
+
+ private SearchResultAggregator() {
+ }
+
+ public static SearchResultAggregator getInstance() {
+ if (sResultAggregator == null) {
+ sResultAggregator = new SearchResultAggregator();
+ }
+
+ return sResultAggregator;
+ }
+
+ @NonNull
+ public synchronized List<? extends SearchResult> fetchResults(Context context, String query) {
+ SearchFeatureProvider mFeatureProvider = FeatureFactory.getFactory(
+ context).getSearchFeatureProvider();
+ ExecutorService executorService = mFeatureProvider.getExecutorService();
+
+ final DatabaseResultLoader staticResultsTask =
+ mFeatureProvider.getStaticSearchResultTask(context, query);
+ final InstalledAppResultLoader installedAppTask =
+ mFeatureProvider.getInstalledAppSearchTask(context, query);
+ final InputDeviceResultLoader inputDevicesTask =
+ mFeatureProvider.getInputDeviceResultTask(context, query);
+ final AccessibilityServiceResultLoader accessibilityServicesTask =
+ mFeatureProvider.getAccessibilityServiceResultTask(context,
+ query);
+
+ executorService.execute(staticResultsTask);
+ executorService.execute(installedAppTask);
+ executorService.execute(inputDevicesTask);
+ executorService.execute(accessibilityServicesTask);
+
+ SparseArray<List<? extends SearchResult>> resultsArray = new SparseArray<>();
+ List<? extends SearchResult> EMPTY_LIST = new ArrayList<>();
+
+ long allTasksStart = System.currentTimeMillis();
+ try {
+ resultsArray.put(ResultLoaderId.INPUT_RESULTS,
+ inputDevicesTask.get(SHORT_CHECK_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } catch (TimeoutException | InterruptedException | ExecutionException e) {
+ Log.d(TAG, "Could not retrieve input devices results in time: " + e);
+ resultsArray.put(ResultLoaderId.INPUT_RESULTS, EMPTY_LIST);
+ }
+
+ try {
+ resultsArray.put(ResultLoaderId.ACCESSIBILITY_RESULTS,
+ accessibilityServicesTask.get(SHORT_CHECK_TASK_TIMEOUT_MS,
+ TimeUnit.MILLISECONDS));
+ } catch (TimeoutException | InterruptedException | ExecutionException e) {
+ Log.d(TAG, "Could not retrieve accessibility results in time: " + e);
+ resultsArray.put(ResultLoaderId.ACCESSIBILITY_RESULTS, EMPTY_LIST);
+ }
+
+ try {
+ resultsArray.put(ResultLoaderId.STATIC_RESULTS,
+ staticResultsTask.get(LONG_CHECK_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } catch (TimeoutException | InterruptedException | ExecutionException e) {
+ Log.d(TAG, "Could not retrieve static results: " + e);
+ resultsArray.put(ResultLoaderId.STATIC_RESULTS, EMPTY_LIST);
+ }
+
+ try {
+ resultsArray.put(ResultLoaderId.INSTALLED_RESULTS,
+ installedAppTask.get(SHORT_CHECK_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } catch (TimeoutException | InterruptedException | ExecutionException e) {
+ Log.d(TAG, "Could not retrieve installed app results in time: " + e);
+
+ resultsArray.put(ResultLoaderId.INSTALLED_RESULTS, EMPTY_LIST);
+ }
+
+ long mergeStartTime = System.currentTimeMillis();
+ Log.i(TAG, "Total result loader time: " + (mergeStartTime - allTasksStart));
+ List<? extends SearchResult> mergedResults = mergeSearchResults(resultsArray);
+ Log.i(TAG, "Total merge time: " + (System.currentTimeMillis() - mergeStartTime));
+ Log.i(TAG, "Total aggregator time: " + (System.currentTimeMillis() - allTasksStart));
+
+ return mergedResults;
+ }
+
+ // TODO (b/68255021) scale the dynamic search results ranks and do a k-way merge
+ private List<? extends SearchResult> mergeSearchResults(
+ SparseArray<List<? extends SearchResult>> resultsArray) {
+ List<? extends SearchResult> staticResults = resultsArray.get(
+ ResultLoaderId.STATIC_RESULTS);
+ List<? extends SearchResult> installedAppResults = resultsArray.get(
+ ResultLoaderId.INSTALLED_RESULTS);
+ List<? extends SearchResult> accessibilityResults = resultsArray.get(
+ ResultLoaderId.ACCESSIBILITY_RESULTS);
+ List<? extends SearchResult> inputDeviceResults = resultsArray.get(
+ ResultLoaderId.INPUT_RESULTS);
+ List<SearchResult> searchResults;
+
+ int staticSize = staticResults.size();
+ int appSize = installedAppResults.size();
+ int a11ySize = accessibilityResults.size();
+ int inputDeviceSize = inputDeviceResults.size();
+ int appIndex = 0;
+ int a11yIndex = 0;
+ int inputDeviceIndex = 0;
+ int rank = SearchResult.TOP_RANK;
+
+ // TODO: We need a helper method to do k-way merge.
+ searchResults = new ArrayList<>(staticSize + appSize + a11ySize + inputDeviceSize);
+ searchResults.addAll(resultsArray.get(ResultLoaderId.STATIC_RESULTS));
+
+ while (rank <= SearchResult.BOTTOM_RANK) {
+ while ((appIndex < appSize) && (installedAppResults.get(appIndex).rank == rank)) {
+ searchResults.add(installedAppResults.get(appIndex++));
+ }
+ while ((a11yIndex < a11ySize) && (accessibilityResults.get(a11yIndex).rank == rank)) {
+ searchResults.add(accessibilityResults.get(a11yIndex++));
+ }
+ while (inputDeviceIndex < inputDeviceSize
+ && inputDeviceResults.get(inputDeviceIndex).rank == rank) {
+ searchResults.add(inputDeviceResults.get(inputDeviceIndex++));
+ }
+ rank++;
+ }
+
+ while (appIndex < appSize) {
+ searchResults.add(installedAppResults.get(appIndex++));
+ }
+ while (a11yIndex < a11ySize) {
+ searchResults.add(accessibilityResults.get(a11yIndex++));
+ }
+ while (inputDeviceIndex < inputDeviceSize) {
+ searchResults.add(inputDeviceResults.get(inputDeviceIndex++));
+ }
+
+ return searchResults;
+ }
+}
diff --git a/src/com/android/settings/search/SearchResultDiffCallback.java b/src/com/android/settings/search/SearchResultDiffCallback.java
index b7bbc66..0f0b977 100644
--- a/src/com/android/settings/search/SearchResultDiffCallback.java
+++ b/src/com/android/settings/search/SearchResultDiffCallback.java
@@ -26,10 +26,11 @@
*/
public class SearchResultDiffCallback extends DiffUtil.Callback {
- private List<SearchResult> mOldList;
- private List<SearchResult> mNewList;
+ private List<? extends SearchResult> mOldList;
+ private List<? extends SearchResult> mNewList;
- public SearchResultDiffCallback(List<SearchResult> oldList, List<SearchResult> newList) {
+ public SearchResultDiffCallback(List<? extends SearchResult> oldList,
+ List<? extends SearchResult> newList) {
mOldList = oldList;
mNewList = newList;
}
diff --git a/src/com/android/settings/search/SearchResultLoader.java b/src/com/android/settings/search/SearchResultLoader.java
new file mode 100644
index 0000000..7ec3146
--- /dev/null
+++ b/src/com/android/settings/search/SearchResultLoader.java
@@ -0,0 +1,30 @@
+package com.android.settings.search;
+
+import com.android.settings.utils.AsyncLoader;
+
+import android.content.Context;
+
+import java.util.List;
+
+/**
+ * Loads a sorted list of Search results for a given query.
+ */
+public class SearchResultLoader extends AsyncLoader<List<? extends SearchResult>> {
+
+ private final String mQuery;
+
+ public SearchResultLoader(Context context, String query) {
+ super(context);
+ mQuery = query;
+ }
+
+ @Override
+ public List<? extends SearchResult> loadInBackground() {
+ SearchResultAggregator aggregator = SearchResultAggregator.getInstance();
+ return aggregator.fetchResults(getContext(), mQuery);
+ }
+
+ @Override
+ protected void onDiscardResult(List<? extends SearchResult> result) {
+ }
+}
diff --git a/src/com/android/settings/search/SearchResultsAdapter.java b/src/com/android/settings/search/SearchResultsAdapter.java
index 5fedc52..c05ce18 100644
--- a/src/com/android/settings/search/SearchResultsAdapter.java
+++ b/src/com/android/settings/search/SearchResultsAdapter.java
@@ -18,87 +18,25 @@
package com.android.settings.search;
import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.support.annotation.IntDef;
-import android.support.annotation.MainThread;
-import android.support.annotation.VisibleForTesting;
import android.support.v7.util.DiffUtil;
import android.support.v7.widget.RecyclerView;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.settings.R;
-import com.android.settings.search.ranking.SearchResultsRankerCallback;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder>
- implements SearchResultsRankerCallback {
- private static final String TAG = "SearchResultsAdapter";
-
- @VisibleForTesting
- static final String DB_RESULTS_LOADER_KEY = DatabaseResultLoader.class.getName();
-
- @VisibleForTesting
- static final String APP_RESULTS_LOADER_KEY = InstalledAppResultLoader.class.getName();
- @VisibleForTesting
- static final String ACCESSIBILITY_LOADER_KEY = AccessibilityServiceResultLoader.class.getName();
- @VisibleForTesting
- static final String INPUT_DEVICE_LOADER_KEY = InputDeviceResultLoader.class.getName();
-
- @VisibleForTesting
- static final int MSG_RANKING_TIMED_OUT = 1;
+public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder> {
private final SearchFragment mFragment;
- private final Context mContext;
private final List<SearchResult> mSearchResults;
- private final List<SearchResult> mStaticallyRankedSearchResults;
- private Map<String, Set<? extends SearchResult>> mResultsMap;
- private final SearchFeatureProvider mSearchFeatureProvider;
- private List<Pair<String, Float>> mSearchRankingScores;
- private Handler mHandler;
- private boolean mSearchResultsLoaded;
- private boolean mSearchResultsUpdated;
- @IntDef({DISABLED, PENDING_RESULTS, SUCCEEDED, FAILED, TIMED_OUT})
- @Retention(RetentionPolicy.SOURCE)
- private @interface AsyncRankingState {}
- @VisibleForTesting
- static final int DISABLED = 0;
- @VisibleForTesting
- static final int PENDING_RESULTS = 1;
- @VisibleForTesting
- static final int SUCCEEDED = 2;
- @VisibleForTesting
- static final int FAILED = 3;
- @VisibleForTesting
- static final int TIMED_OUT = 4;
- private @AsyncRankingState int mAsyncRankingState;
-
- public SearchResultsAdapter(SearchFragment fragment,
- SearchFeatureProvider searchFeatureProvider) {
+ public SearchResultsAdapter(SearchFragment fragment) {
mFragment = fragment;
- mContext = fragment.getContext().getApplicationContext();
mSearchResults = new ArrayList<>();
- mResultsMap = new ArrayMap<>();
- mSearchRankingScores = new ArrayList<>();
- mStaticallyRankedSearchResults = new ArrayList<>();
- mSearchFeatureProvider = searchFeatureProvider;
setHasStableIds(true);
}
@@ -149,298 +87,30 @@
return mSearchResults.size();
}
- @MainThread
- @Override
- public void onRankingScoresAvailable(List<Pair<String, Float>> searchRankingScores) {
- // Received the scores, stop the timeout timer.
- getHandler().removeMessages(MSG_RANKING_TIMED_OUT);
- if (mAsyncRankingState == PENDING_RESULTS) {
- mAsyncRankingState = SUCCEEDED;
- mSearchRankingScores.clear();
- mSearchRankingScores.addAll(searchRankingScores);
- if (canUpdateSearchResults()) {
- updateSearchResults();
- }
- } else {
- Log.w(TAG, "Ranking scores became available in invalid state: " + mAsyncRankingState);
- }
- }
-
- @MainThread
- @Override
- public void onRankingFailed() {
- if (mAsyncRankingState == PENDING_RESULTS) {
- mAsyncRankingState = FAILED;
- if (canUpdateSearchResults()) {
- updateSearchResults();
- }
- } else {
- Log.w(TAG, "Ranking scores failed in invalid states: " + mAsyncRankingState);
- }
- }
-
- /**
- * Store the results from each of the loaders to be merged when all loaders are finished.
- *
- * @param results the results from the loader.
- * @param loaderClassName class name of the loader.
- */
- @MainThread
- public void addSearchResults(Set<? extends SearchResult> results, String loaderClassName) {
- if (results == null) {
- return;
- }
- mResultsMap.put(loaderClassName, results);
- }
-
/**
* Displays recent searched queries.
- *
- * @return The number of saved queries to display
*/
- public int displaySavedQuery(List<? extends SearchResult> data) {
+ public void displaySavedQuery(List<? extends SearchResult> data) {
clearResults();
mSearchResults.addAll(data);
notifyDataSetChanged();
- return mSearchResults.size();
- }
-
- /**
- * Notifies the adapter that all the unsorted results are loaded and now the ladapter can
- * proceed with ranking the results.
- */
- @MainThread
- public void notifyResultsLoaded() {
- mSearchResultsLoaded = true;
- // static ranking is skipped only if asyc ranking is already succeeded.
- if (mAsyncRankingState != SUCCEEDED) {
- doStaticRanking();
- }
- if (canUpdateSearchResults()) {
- updateSearchResults();
- }
}
public void clearResults() {
mSearchResults.clear();
- mStaticallyRankedSearchResults.clear();
- mResultsMap.clear();
notifyDataSetChanged();
}
- @VisibleForTesting
public List<SearchResult> getSearchResults() {
return mSearchResults;
}
- @MainThread
- public void initializeSearch(String query) {
- clearResults();
- mSearchResultsLoaded = false;
- mSearchResultsUpdated = false;
- if (mSearchFeatureProvider.isSmartSearchRankingEnabled(mContext)) {
- mAsyncRankingState = PENDING_RESULTS;
- mSearchFeatureProvider.cancelPendingSearchQuery(mContext);
- final Handler handler = getHandler();
- final long timeoutMs = mSearchFeatureProvider.smartSearchRankingTimeoutMs(mContext);
- handler.sendMessageDelayed(
- handler.obtainMessage(MSG_RANKING_TIMED_OUT), timeoutMs);
- mSearchFeatureProvider.querySearchResults(mContext, query, this);
- } else {
- mAsyncRankingState = DISABLED;
- }
- }
-
- @AsyncRankingState int getAsyncRankingState() {
- return mAsyncRankingState;
- }
-
- /**
- * Merge the results from each of the loaders into one list for the adapter.
- * Prioritizes results from the local database over installed apps.
- */
- private void doStaticRanking() {
- List<? extends SearchResult> databaseResults =
- getSortedLoadedResults(DB_RESULTS_LOADER_KEY);
- List<? extends SearchResult> installedAppResults =
- getSortedLoadedResults(APP_RESULTS_LOADER_KEY);
- List<? extends SearchResult> accessibilityResults =
- getSortedLoadedResults(ACCESSIBILITY_LOADER_KEY);
- List<? extends SearchResult> inputDeviceResults =
- getSortedLoadedResults(INPUT_DEVICE_LOADER_KEY);
-
- int dbSize = databaseResults.size();
- int appSize = installedAppResults.size();
- int a11ySize = accessibilityResults.size();
- int inputDeviceSize = inputDeviceResults.size();
- int dbIndex = 0;
- int appIndex = 0;
- int a11yIndex = 0;
- int inputDeviceIndex = 0;
- int rank = SearchResult.TOP_RANK;
-
- // TODO: We need a helper method to do k-way merge.
- mStaticallyRankedSearchResults.clear();
- while (rank <= SearchResult.BOTTOM_RANK) {
- while ((dbIndex < dbSize) && (databaseResults.get(dbIndex).rank == rank)) {
- mStaticallyRankedSearchResults.add(databaseResults.get(dbIndex++));
- }
- while ((appIndex < appSize) && (installedAppResults.get(appIndex).rank == rank)) {
- mStaticallyRankedSearchResults.add(installedAppResults.get(appIndex++));
- }
- while ((a11yIndex < a11ySize) && (accessibilityResults.get(a11yIndex).rank == rank)) {
- mStaticallyRankedSearchResults.add(accessibilityResults.get(a11yIndex++));
- }
- while (inputDeviceIndex < inputDeviceSize
- && inputDeviceResults.get(inputDeviceIndex).rank == rank) {
- mStaticallyRankedSearchResults.add(inputDeviceResults.get(inputDeviceIndex++));
- }
- rank++;
- }
-
- while (dbIndex < dbSize) {
- mStaticallyRankedSearchResults.add(databaseResults.get(dbIndex++));
- }
- while (appIndex < appSize) {
- mStaticallyRankedSearchResults.add(installedAppResults.get(appIndex++));
- }
- while(a11yIndex < a11ySize) {
- mStaticallyRankedSearchResults.add(accessibilityResults.get(a11yIndex++));
- }
- while (inputDeviceIndex < inputDeviceSize) {
- mStaticallyRankedSearchResults.add(inputDeviceResults.get(inputDeviceIndex++));
- }
- }
-
- private void updateSearchResults() {
- switch (mAsyncRankingState) {
- case PENDING_RESULTS:
- break;
- case DISABLED:
- case FAILED:
- case TIMED_OUT:
- // When DISABLED or FAILED or TIMED_OUT, we use static ranking results.
- postSearchResults(mStaticallyRankedSearchResults, false);
- break;
- case SUCCEEDED:
- postSearchResults(doAsyncRanking(), true);
- break;
- }
- }
-
- private boolean canUpdateSearchResults() {
- // Results are not updated yet and db results are loaded and we are not waiting on async
- // ranking scores.
- return !mSearchResultsUpdated
- && mSearchResultsLoaded
- && mAsyncRankingState != PENDING_RESULTS;
- }
-
- @VisibleForTesting
- List<SearchResult> doAsyncRanking() {
- Set<? extends SearchResult> databaseResults =
- getUnsortedLoadedResults(DB_RESULTS_LOADER_KEY);
- List<? extends SearchResult> installedAppResults =
- getSortedLoadedResults(APP_RESULTS_LOADER_KEY);
- List<? extends SearchResult> accessibilityResults =
- getSortedLoadedResults(ACCESSIBILITY_LOADER_KEY);
- List<? extends SearchResult> inputDeviceResults =
- getSortedLoadedResults(INPUT_DEVICE_LOADER_KEY);
- int dbSize = databaseResults.size();
- int appSize = installedAppResults.size();
- int a11ySize = accessibilityResults.size();
- int inputDeviceSize = inputDeviceResults.size();
-
- final List<SearchResult> asyncRankingResults = new ArrayList<>(
- dbSize + appSize + a11ySize + inputDeviceSize);
- TreeSet<SearchResult> dbResultsSortedByScores = new TreeSet<>(
- new Comparator<SearchResult>() {
- @Override
- public int compare(SearchResult o1, SearchResult o2) {
- float score1 = getRankingScoreByStableId(o1.stableId);
- float score2 = getRankingScoreByStableId(o2.stableId);
- if (score1 > score2) {
- return -1;
- } else if (score1 == score2) {
- return 0;
- } else {
- return 1;
- }
- }
- });
- dbResultsSortedByScores.addAll(databaseResults);
- asyncRankingResults.addAll(dbResultsSortedByScores);
- // Other results are not ranked by async ranking and appended at the end of the list.
- asyncRankingResults.addAll(installedAppResults);
- asyncRankingResults.addAll(accessibilityResults);
- asyncRankingResults.addAll(inputDeviceResults);
- return asyncRankingResults;
- }
-
- @VisibleForTesting
- Set<? extends SearchResult> getUnsortedLoadedResults(String loaderKey) {
- return mResultsMap.containsKey(loaderKey) ? mResultsMap.get(loaderKey) : new HashSet<>();
- }
-
- @VisibleForTesting
- List<? extends SearchResult> getSortedLoadedResults(String loaderKey) {
- List<? extends SearchResult> sortedLoadedResults =
- new ArrayList<>(getUnsortedLoadedResults(loaderKey));
- Collections.sort(sortedLoadedResults);
- return sortedLoadedResults;
- }
-
- /**
- * Looks up ranking score for stableId
- * @param stableId String of stableId
- * @return the ranking score corresponding to the given stableId. If there is no score
- * available for this stableId, -Float.MAX_VALUE is returned.
- */
- @VisibleForTesting
- Float getRankingScoreByStableId(int stableId) {
- for (Pair<String, Float> rankingScore : mSearchRankingScores) {
- if (Integer.toString(stableId).compareTo(rankingScore.first) == 0) {
- return rankingScore.second;
- }
- }
- // If stableId not found in the list, we assign the minimum score so it will appear at
- // the end of the list.
- Log.w(TAG, "stableId " + stableId + " was not in the ranking scores.");
- return -Float.MAX_VALUE;
- }
-
- @VisibleForTesting
- Handler getHandler() {
- if (mHandler == null) {
- mHandler = new Handler(Looper.getMainLooper()) {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == MSG_RANKING_TIMED_OUT) {
- mSearchFeatureProvider.cancelPendingSearchQuery(mContext);
- if (mAsyncRankingState == PENDING_RESULTS) {
- mAsyncRankingState = TIMED_OUT;
- if (canUpdateSearchResults()) {
- updateSearchResults();
- }
- } else {
- Log.w(TAG, "Ranking scores timed out in invalid state: " +
- mAsyncRankingState);
- }
- }
- }
- };
- }
- return mHandler;
- }
-
- @VisibleForTesting
- public void postSearchResults(List<SearchResult> newSearchResults, boolean detectMoves) {
+ public void postSearchResults(List<? extends SearchResult> newSearchResults) {
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(
- new SearchResultDiffCallback(mSearchResults, newSearchResults), detectMoves);
+ new SearchResultDiffCallback(mSearchResults, newSearchResults));
mSearchResults.clear();
mSearchResults.addAll(newSearchResults);
diffResult.dispatchUpdatesTo(this);
mFragment.onSearchResultsDisplayed(mSearchResults.size());
- mSearchResultsUpdated = true;
}
}
diff --git a/src/com/android/settings/EncryptionAndCredential.java b/src/com/android/settings/security/EncryptionAndCredential.java
similarity index 93%
rename from src/com/android/settings/EncryptionAndCredential.java
rename to src/com/android/settings/security/EncryptionAndCredential.java
index 4892f7e..0972e3e 100644
--- a/src/com/android/settings/EncryptionAndCredential.java
+++ b/src/com/android/settings/security/EncryptionAndCredential.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * 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.
@@ -14,13 +14,11 @@
* limitations under the License.
*/
-package com.android.settings;
+package com.android.settings.security;
-import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
-import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.SearchIndexableResource;
@@ -30,11 +28,12 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settings.search.Indexable;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.List;
@@ -43,7 +42,7 @@
* Encryption and Credential settings.
* TODO: Extends this from {@link DashboardFragment} instead
*/
-public class EncryptionAndCredential extends SettingsPreferenceFragment implements Indexable {
+public class EncryptionAndCredential extends DashboardFragment {
private static final String TAG = "EncryptionAndCredential";
@@ -69,12 +68,19 @@
}
@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ protected String getLogTag() {
+ return TAG;
+ }
- final Activity activity = getActivity();
+ @Override
+ protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+ mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ return null;
+ }
- mUm = UserManager.get(activity);
+ @Override
+ protected int getPreferenceScreenResId() {
+ return 0;
}
/**
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index 8ee340a..5a10ebf 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -61,6 +61,7 @@
import com.android.settings.ProxySelector;
import com.android.settings.R;
import com.android.settingslib.Utils;
+import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.wifi.AccessPoint;
import java.net.Inet4Address;
@@ -116,8 +117,6 @@
/* Full list of phase2 methods */
private final ArrayAdapter<String> mPhase2FullAdapter;
- private final Handler mTextViewChangedHandler;
-
// e.g. AccessPoint.SECURITY_NONE
private int mAccessPointSecurity;
private TextView mPasswordView;
@@ -175,7 +174,6 @@
accessPoint.getSecurity();
mMode = mode;
- mTextViewChangedHandler = new Handler();
mContext = mConfigUi.getContext();
final Resources res = mContext.getResources();
@@ -1258,12 +1256,10 @@
@Override
public void afterTextChanged(Editable s) {
- mTextViewChangedHandler.post(new Runnable() {
- public void run() {
- showWarningMessagesIfAppropriate();
- enableSubmitIfAppropriate();
- }
- });
+ ThreadUtils.postOnMainThread(() -> {
+ showWarningMessagesIfAppropriate();
+ enableSubmitIfAppropriate();
+ });
}
@Override
diff --git a/src/com/android/settings/wifi/WriteWifiConfigToNfcDialog.java b/src/com/android/settings/wifi/WriteWifiConfigToNfcDialog.java
index dfed801..dc98e21 100644
--- a/src/com/android/settings/wifi/WriteWifiConfigToNfcDialog.java
+++ b/src/com/android/settings/wifi/WriteWifiConfigToNfcDialog.java
@@ -63,7 +63,6 @@
private View mView;
private Button mSubmitButton;
private Button mCancelButton;
- private Handler mOnTextChangedHandler;
private TextView mPasswordView;
private TextView mLabelView;
private CheckBox mPasswordCheckBox;
@@ -79,7 +78,6 @@
mContext = context;
mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE))
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WriteWifiConfigToNfcDialog:wakeLock");
- mOnTextChangedHandler = new Handler();
mSecurity = security;
mWifiManager = wifiManager;
}
@@ -90,7 +88,6 @@
mContext = context;
mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE))
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WriteWifiConfigToNfcDialog:wakeLock");
- mOnTextChangedHandler = new Handler();
mSecurity = savedState.getInt(SECURITY);
mWifiManager = wifiManager;
}
@@ -226,12 +223,7 @@
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
- mOnTextChangedHandler.post(new Runnable() {
- @Override
- public void run() {
- enableSubmitIfAppropriate();
- }
- });
+ enableSubmitIfAppropriate();
}
private void enableSubmitIfAppropriate() {
diff --git a/tests/anomaly-tester/res/values/strings.xml b/tests/anomaly-tester/res/values/strings.xml
index 99de34e..da56356 100644
--- a/tests/anomaly-tester/res/values/strings.xml
+++ b/tests/anomaly-tester/res/values/strings.xml
@@ -15,5 +15,5 @@
-->
<resources>
- <string name="app_name">AnomalyTester</string>
+ <string name="app_name" translatable="false">AnomalyTester</string>
</resources>
diff --git a/tests/robotests/README.md b/tests/robotests/README.md
new file mode 100644
index 0000000..648f1af
--- /dev/null
+++ b/tests/robotests/README.md
@@ -0,0 +1,24 @@
+# Running Settings Robolectric tests
+
+
+## The full suite
+```
+$ croot
+$ make RunSettingsRoboTests
+```
+
+## Running a single test class
+
+```
+$ croot
+$ make RunSettingsRoboTests ROBOTEST_FILTER=<ClassName>
+```
+
+For example:
+
+```
+make RunSettingsRoboTests ROBOTEST_FILTER=CodeInspectionTest
+```
+
+You can also use partial class name in ROBOTEST_FILTER. If the partial class name matches
+multiple file names, all of them will be executed.
diff --git a/tests/robotests/src/com/android/settings/core/InstrumentedPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/core/InstrumentedPreferenceFragmentTest.java
index ab7228c..30d60cc 100644
--- a/tests/robotests/src/com/android/settings/core/InstrumentedPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/core/InstrumentedPreferenceFragmentTest.java
@@ -74,17 +74,6 @@
}
@Test
- public void onCreatePreferences_preferenceScreenTitleFeatureOff_shouldNotAddPreference() {
- SettingsShadowSystemProperties.set(
- FeatureFlagUtils.FFLAG_PREFIX + mFragment.FEATURE_FLAG_USE_PREFERENCE_SCREEN_TITLE,
- "false");
-
- mFragment.onCreatePreferences(Bundle.EMPTY, null /* rootKey */);
-
- verify(mFragment, never()).addPreferencesFromResource(anyInt());
- }
-
- @Test
public void onCreatePreferences_noPreferenceScreenResId_shouldNotAddPreference() {
SettingsShadowSystemProperties.set(
FeatureFlagUtils.FFLAG_PREFIX + mFragment.FEATURE_FLAG_USE_PREFERENCE_SCREEN_TITLE,
diff --git a/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollEnrollingTest.java b/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollEnrollingTest.java
new file mode 100644
index 0000000..ace535e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fingerprint/FingerprintEnrollEnrollingTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.settings.fingerprint;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.robolectric.RuntimeEnvironment.application;
+
+import android.content.Intent;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback;
+import android.media.AudioAttributes;
+import android.os.CancellationSignal;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.password.ChooseLockSettingsHelper;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settings.testutils.shadow.ShadowVibrator;
+import com.android.settings.wrapper.FingerprintManagerWrapper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+
+import java.util.concurrent.TimeUnit;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(
+ manifest = TestConfig.MANIFEST_PATH,
+ sdk = Config.NEWEST_SDK,
+ shadows = {
+ ShadowUtils.class,
+ ShadowVibrator.class
+ })
+public class FingerprintEnrollEnrollingTest {
+
+ @Mock
+ private FingerprintManagerWrapper mFingerprintManager;
+
+ private FingerprintEnrollEnrolling mActivity;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ ShadowUtils.setFingerprintManager(mFingerprintManager);
+ ShadowVibrator.addToServiceMap();
+
+ mActivity = Robolectric.buildActivity(
+ FingerprintEnrollEnrolling.class,
+ new Intent()
+ // Set the challenge token so the confirm screen will not be shown
+ .putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]))
+ .setup().get();
+ }
+
+ @After
+ public void tearDown() {
+ ShadowUtils.reset();
+ ShadowVibrator.reset();
+ }
+
+ @Test
+ public void fingerprintEnrollHelp_shouldShowHelpTextAndVibrate() {
+ EnrollmentCallback enrollmentCallback = verifyAndCaptureEnrollmentCallback();
+
+ enrollmentCallback.onEnrollmentProgress(123);
+ enrollmentCallback.onEnrollmentHelp(
+ FingerprintManager.FINGERPRINT_ERROR_UNABLE_TO_PROCESS,
+ "test enrollment help");
+
+ TextView errorText = mActivity.findViewById(R.id.error_text);
+ assertThat(errorText.getText()).isEqualTo("test enrollment help");
+
+ Robolectric.getForegroundThreadScheduler().advanceBy(2, TimeUnit.MILLISECONDS);
+
+
+ ShadowVibrator shadowVibrator =
+ Shadow.extract(application.getSystemService(Vibrator.class));
+ verify(shadowVibrator.delegate).vibrate(
+ anyInt(),
+ nullable(String.class),
+ any(VibrationEffect.class),
+ nullable(AudioAttributes.class));
+ }
+
+ private EnrollmentCallback verifyAndCaptureEnrollmentCallback() {
+ ArgumentCaptor<EnrollmentCallback> callbackCaptor =
+ ArgumentCaptor.forClass(EnrollmentCallback.class);
+ verify(mFingerprintManager).enroll(
+ any(byte[].class),
+ any(CancellationSignal.class),
+ anyInt(),
+ anyInt(),
+ callbackCaptor.capture());
+
+ return callbackCaptor.getValue();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/search/AccessibilityServiceResultLoaderTest.java b/tests/robotests/src/com/android/settings/search/AccessibilityServiceResultFutureTaskTest.java
similarity index 83%
rename from tests/robotests/src/com/android/settings/search/AccessibilityServiceResultLoaderTest.java
rename to tests/robotests/src/com/android/settings/search/AccessibilityServiceResultFutureTaskTest.java
index 4896dc4..0e4abe1 100644
--- a/tests/robotests/src/com/android/settings/search/AccessibilityServiceResultLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/search/AccessibilityServiceResultFutureTaskTest.java
@@ -17,6 +17,7 @@
package com.android.settings.search;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -46,7 +47,7 @@
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class AccessibilityServiceResultLoaderTest {
+public class AccessibilityServiceResultFutureTaskTest {
private static final String QUERY = "test_query";
@@ -59,7 +60,7 @@
@Mock
private SiteMapManager mSiteMapManager;
- private AccessibilityServiceResultLoader mLoader;
+ private AccessibilityServiceResultLoader.AccessibilityServiceResultCallable mCallable;
@Before
public void setUp() {
@@ -68,19 +69,20 @@
.thenReturn(mAccessibilityManager);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
- mLoader = new AccessibilityServiceResultLoader(mContext, QUERY, mSiteMapManager);
+ mCallable = new AccessibilityServiceResultLoader.AccessibilityServiceResultCallable(
+ mContext, QUERY, mSiteMapManager);
}
@Test
- public void query_noService_shouldNotReturnAnything() {
- assertThat(mLoader.loadInBackground()).isEmpty();
+ public void query_noService_shouldNotReturnAnything() throws Exception {
+ assertThat(mCallable.call()).isEmpty();
}
@Test
- public void query_hasServiceMatchingTitle_shouldReturnResult() {
+ public void query_hasServiceMatchingTitle_shouldReturnResult() throws Exception {
addFakeAccessibilityService();
- List<? extends SearchResult> results = new ArrayList<>(mLoader.loadInBackground());
+ List<? extends SearchResult> results = mCallable.call();
assertThat(results).hasSize(1);
SearchResult result = results.get(0);
@@ -88,13 +90,14 @@
}
@Test
- public void query_serviceDoesNotMatchTitle_shouldReturnResult() {
+ public void query_serviceDoesNotMatchTitle_shouldReturnResult() throws Exception {
addFakeAccessibilityService();
- mLoader = new AccessibilityServiceResultLoader(mContext,
+ mCallable = new AccessibilityServiceResultLoader.AccessibilityServiceResultCallable(
+ mContext,
QUERY + "no_match", mSiteMapManager);
- assertThat(mLoader.loadInBackground()).isEmpty();
+ assertThat(mCallable.call()).isEmpty();
}
private void addFakeAccessibilityService() {
diff --git a/tests/robotests/src/com/android/settings/search/InputDeviceResultLoaderTest.java b/tests/robotests/src/com/android/settings/search/InputDeviceResultFutureTaskTest.java
similarity index 87%
rename from tests/robotests/src/com/android/settings/search/InputDeviceResultLoaderTest.java
rename to tests/robotests/src/com/android/settings/search/InputDeviceResultFutureTaskTest.java
index a955af1..e31b3d7 100644
--- a/tests/robotests/src/com/android/settings/search/InputDeviceResultLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/search/InputDeviceResultFutureTaskTest.java
@@ -17,9 +17,11 @@
package com.android.settings.search;
import static android.content.Context.INPUT_METHOD_SERVICE;
+
import static com.android.settings.search.InputDeviceResultLoader.PHYSICAL_KEYBOARD_FRAGMENT;
import static com.android.settings.search.InputDeviceResultLoader.VIRTUAL_KEYBOARD_FRAGMENT;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -58,7 +60,7 @@
shadows = {
ShadowInputDevice.class
})
-public class InputDeviceResultLoaderTest {
+public class InputDeviceResultFutureTaskTest {
private static final String QUERY = "test_query";
private static final List<String> PHYSICAL_KEYBOARD_BREADCRUMB;
@@ -84,7 +86,7 @@
@Mock
private PackageManager mPackageManager;
- private InputDeviceResultLoader mLoader;
+ private InputDeviceResultLoader.InputDeviceResultCallable mCallable;
@Before
public void setUp() {
@@ -99,7 +101,8 @@
when(mContext.getString(anyInt()))
.thenAnswer(invocation -> RuntimeEnvironment.application.getString(
(Integer) invocation.getArguments()[0]));
- mLoader = new InputDeviceResultLoader(mContext, QUERY, mSiteMapManager);
+ mCallable = new InputDeviceResultLoader.InputDeviceResultCallable(mContext, QUERY,
+ mSiteMapManager);
}
@After
@@ -108,18 +111,19 @@
}
@Test
- public void query_noKeyboard_shouldNotReturnAnything() {
- assertThat(mLoader.loadInBackground()).isEmpty();
+ public void query_noKeyboard_shouldNotReturnAnything() throws Exception {
+
+ assertThat(mCallable.call()).isEmpty();
}
@Test
- public void query_hasPhysicalKeyboard_match() {
+ public void query_hasPhysicalKeyboard_match() throws Exception {
addPhysicalKeyboard(QUERY);
when(mSiteMapManager.buildBreadCrumb(mContext, PHYSICAL_KEYBOARD_FRAGMENT,
RuntimeEnvironment.application.getString(R.string.physical_keyboard_title)))
.thenReturn(PHYSICAL_KEYBOARD_BREADCRUMB);
- final List<SearchResult> results = new ArrayList<>(mLoader.loadInBackground());
+ final List<? extends SearchResult> results = mCallable.call();
assertThat(results).hasSize(1);
assertThat(results.get(0).title).isEqualTo(QUERY);
@@ -128,13 +132,13 @@
}
@Test
- public void query_hasVirtualKeyboard_match() {
+ public void query_hasVirtualKeyboard_match() throws Exception {
addVirtualKeyboard(QUERY);
when(mSiteMapManager.buildBreadCrumb(mContext, VIRTUAL_KEYBOARD_FRAGMENT,
RuntimeEnvironment.application.getString(R.string.add_virtual_keyboard)))
.thenReturn(VIRTUAL_KEYBOARD_BREADCRUMB);
- final List<SearchResult> results = new ArrayList<>(mLoader.loadInBackground());
+ final List<? extends SearchResult> results = mCallable.call();
assertThat(results).hasSize(1);
assertThat(results.get(0).title).isEqualTo(QUERY);
assertThat(results.get(0).breadcrumbs)
@@ -142,11 +146,11 @@
}
@Test
- public void query_hasPhysicalVirtualKeyboard_doNotMatch() {
+ public void query_hasPhysicalVirtualKeyboard_doNotMatch() throws Exception {
addPhysicalKeyboard("abc");
addVirtualKeyboard("def");
- assertThat(mLoader.loadInBackground()).isEmpty();
+ assertThat(mCallable.call()).isEmpty();
verifyZeroInteractions(mSiteMapManager);
}
@@ -170,4 +174,4 @@
when(mImm.getInputMethodList()).thenReturn(imis);
}
-}
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/search/InstalledAppResultLoaderTest.java b/tests/robotests/src/com/android/settings/search/InstalledAppResultLoaderTest.java
index f1a25a1..0e84dc7 100644
--- a/tests/robotests/src/com/android/settings/search/InstalledAppResultLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/search/InstalledAppResultLoaderTest.java
@@ -19,7 +19,9 @@
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyList;
@@ -53,8 +55,6 @@
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
@@ -77,7 +77,7 @@
@Mock
private SiteMapManager mSiteMapManager;
- private InstalledAppResultLoader mLoader;
+ private InstalledAppResultLoader.InstalledAppResultCallable mCallable;
@Before
public void setUp() {
@@ -109,49 +109,50 @@
}
@Test
- public void query_noMatchingQuery_shouldReturnEmptyResult() {
+ public void query_noMatchingQuery_shouldReturnEmptyResult() throws Exception {
final String query = "abc";
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground()).isEmpty();
+ assertThat(mCallable.call()).isEmpty();
}
@Test
- public void query_matchingQuery_shouldReturnNonSystemApps() {
+ public void query_matchingQuery_shouldReturnNonSystemApps() throws Exception {
final String query = "app";
- mLoader = spy(new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = spy(new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager));
- when(mLoader.getContext()).thenReturn(mContext);
when(mSiteMapManager.buildBreadCrumb(eq(mContext), anyString(), anyString()))
.thenReturn(Arrays.asList(new String[]{"123"}));
- assertThat(mLoader.loadInBackground().size()).isEqualTo(3);
+ assertThat(mCallable.call()).hasSize(3);
verify(mSiteMapManager)
.buildBreadCrumb(eq(mContext), anyString(), anyString());
}
@Test
- public void query_matchingQuery_shouldReturnSystemAppUpdates() {
+ public void query_matchingQuery_shouldReturnSystemAppUpdates() throws Exception {
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, "app1", FLAG_UPDATED_SYSTEM_APP,
0 /* targetSdkVersion */)));
final String query = "app";
- mLoader = spy(new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = spy(new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager));
- when(mLoader.getContext()).thenReturn(mContext);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
verify(mSiteMapManager)
.buildBreadCrumb(eq(mContext), anyString(), anyString());
}
@Test
- public void query_matchingQuery_shouldReturnSystemAppIfLaunchable() {
+ public void query_matchingQuery_shouldReturnSystemAppIfLaunchable() throws Exception {
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, "app1", FLAG_SYSTEM,
@@ -164,14 +165,15 @@
final String query = "app";
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_matchingQuery_shouldReturnSystemAppIfHomeApp() {
+ public void query_matchingQuery_shouldReturnSystemAppIfHomeApp() throws Exception {
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, "app1", FLAG_SYSTEM,
@@ -180,28 +182,26 @@
any(Intent.class), anyInt(), anyInt()))
.thenReturn(null);
- when(mPackageManagerWrapper.getHomeActivities(anyList())).thenAnswer(new Answer<Object>() {
- @Override
- public Object answer(InvocationOnMock invocation) throws Throwable {
- final List<ResolveInfo> list = (List<ResolveInfo>) invocation.getArguments()[0];
- final ResolveInfo info = new ResolveInfo();
- info.activityInfo = new ActivityInfo();
- info.activityInfo.packageName = "app1";
- list.add(info);
- return null;
- }
+ when(mPackageManagerWrapper.getHomeActivities(anyList())).thenAnswer(invocation -> {
+ final List<ResolveInfo> list = (List<ResolveInfo>) invocation.getArguments()[0];
+ final ResolveInfo info = new ResolveInfo();
+ info.activityInfo = new ActivityInfo();
+ info.activityInfo.packageName = "app1";
+ list.add(info);
+ return null;
});
final String query = "app";
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_matchingQuery_shouldNotReturnSystemAppIfNotLaunchable() {
+ public void query_matchingQuery_shouldNotReturnSystemAppIfNotLaunchable() throws Exception {
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, "app1", FLAG_SYSTEM,
@@ -212,21 +212,23 @@
final String query = "app";
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground()).isEmpty();
+ assertThat(mCallable.call()).isEmpty();
verify(mSiteMapManager, never())
.buildBreadCrumb(eq(mContext), anyString(), anyString());
}
@Test
- public void query_matchingQuery_multipleResults() {
+ public void query_matchingQuery_multipleResults() throws Exception {
final String query = "app";
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- final Set<? extends SearchResult> results = mLoader.loadInBackground();
+ final List<? extends SearchResult> results = mCallable.call();
Set<CharSequence> expectedTitles = new HashSet<>(Arrays.asList("app4", "app", "appBuffer"));
Set<CharSequence> actualTitles = new HashSet<>();
@@ -237,161 +239,172 @@
}
@Test
- public void query_normalWord_MatchPrefix() {
+ public void query_normalWord_MatchPrefix() throws Exception {
final String query = "ba";
final String packageName = "Bananas";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_CapitalCase_DoestMatchSecondWord() {
+ public void query_CapitalCase_DoestMatchSecondWord() throws Exception {
final String query = "Apples";
final String packageName = "BananasApples";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(0);
+ assertThat(mCallable.call()).isEmpty();
}
@Test
- public void query_TwoWords_MatchesFirstWord() {
+ public void query_TwoWords_MatchesFirstWord() throws Exception {
final String query = "Banana";
final String packageName = "Bananas Apples";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_TwoWords_MatchesSecondWord() {
+ public void query_TwoWords_MatchesSecondWord() throws Exception {
final String query = "Apple";
final String packageName = "Bananas Apples";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_ThreeWords_MatchesThirdWord() {
+ public void query_ThreeWords_MatchesThirdWord() throws Exception {
final String query = "Pear";
final String packageName = "Bananas Apples Pears";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_DoubleSpacedWords_MatchesSecondWord() {
+ public void query_DoubleSpacedWords_MatchesSecondWord() throws Exception {
final String query = "Apple";
final String packageName = "Bananas Apples";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_SpecialChar_MatchesSecondWord() {
+ public void query_SpecialChar_MatchesSecondWord() throws Exception {
final String query = "Apple";
final String packageName = "Bananas & Apples";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_TabSeparated_MatchesSecondWord() {
+ public void query_TabSeparated_MatchesSecondWord() throws Exception {
final String query = "Apple";
final String packageName = "Bananas\tApples";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_LeadingNumber_MatchesWord() {
+ public void query_LeadingNumber_MatchesWord() throws Exception {
final String query = "4";
final String packageName = "4Bananas";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(1);
+ assertThat(mCallable.call()).hasSize(1);
}
@Test
- public void query_FirstWordPrefixOfQuery_NoMatch() {
+ public void query_FirstWordPrefixOfQuery_NoMatch() throws Exception {
final String query = "Bananass";
final String packageName = "Bananas Apples";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(0);
+ assertThat(mCallable.call()).isEmpty();
}
@Test
- public void query_QueryLongerThanAppName_NoMatch() {
+ public void query_QueryLongerThanAppName_NoMatch() throws Exception {
final String query = "BananasApples";
final String packageName = "Bananas";
when(mPackageManagerWrapper.getInstalledApplicationsAsUser(anyInt(), anyInt()))
.thenReturn(Arrays.asList(
ApplicationTestUtils.buildInfo(0 /* uid */, packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- assertThat(mLoader.loadInBackground().size()).isEqualTo(0);
+ assertThat(mCallable.call()).isEmpty();
}
@Test
- public void query_appExistsInBothProfiles() {
+ public void query_appExistsInBothProfiles() throws Exception {
final String query = "carrot";
final String packageName = "carrot";
final int user1 = 0;
@@ -414,10 +427,11 @@
packageName, 0 /* flags */,
0 /* targetSdkVersion */)));
- mLoader = new InstalledAppResultLoader(mContext, mPackageManagerWrapper, query,
+ mCallable = new InstalledAppResultLoader.InstalledAppResultCallable(mContext,
+ mPackageManagerWrapper, query,
mSiteMapManager);
- Set<AppSearchResult> searchResults = (Set<AppSearchResult>) mLoader.loadInBackground();
+ List<AppSearchResult> searchResults = (List<AppSearchResult>) mCallable.call();
assertThat(searchResults).hasSize(2);
Set<Integer> uidResults = searchResults.stream().map(result -> result.info.uid).collect(
diff --git a/tests/robotests/src/com/android/settings/search/MockAccessibilityLoader.java b/tests/robotests/src/com/android/settings/search/MockAccessibilityLoader.java
deleted file mode 100644
index 0a06a35..0000000
--- a/tests/robotests/src/com/android/settings/search/MockAccessibilityLoader.java
+++ /dev/null
@@ -1,39 +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.settings.search;
-
-import android.content.Context;
-
-import java.util.HashSet;
-import java.util.Set;
-
-public class MockAccessibilityLoader extends AccessibilityServiceResultLoader {
-
- public MockAccessibilityLoader(Context context) {
- super(context, "test_query", null);
- }
-
- @Override
- public Set<? extends SearchResult> loadInBackground() {
- return new HashSet<>();
- }
-
- @Override
- protected void onDiscardResult(Set<? extends SearchResult> result) {
-
- }
-}
diff --git a/tests/robotests/src/com/android/settings/search/MockAppLoader.java b/tests/robotests/src/com/android/settings/search/MockAppLoader.java
deleted file mode 100644
index c68cbdf..0000000
--- a/tests/robotests/src/com/android/settings/search/MockAppLoader.java
+++ /dev/null
@@ -1,46 +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.settings.search;
-
-import android.content.Context;
-import com.android.settings.search.InstalledAppResultLoader;
-import com.android.settings.search.SearchResult;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Mock loader to subvert the requirements of returning data while also driving the Loader
- * lifecycle.
- */
-class MockAppLoader extends InstalledAppResultLoader {
-
- public MockAppLoader(Context context) {
- super(context, null, "", null);
- }
-
- @Override
- public Set<? extends SearchResult> loadInBackground() {
- return new HashSet<>();
- }
-
- @Override
- protected void onDiscardResult(Set<? extends SearchResult> result) {
-
- }
-}
diff --git a/tests/robotests/src/com/android/settings/search/MockDBLoader.java b/tests/robotests/src/com/android/settings/search/MockDBLoader.java
deleted file mode 100644
index b28c1ed..0000000
--- a/tests/robotests/src/com/android/settings/search/MockDBLoader.java
+++ /dev/null
@@ -1,46 +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.settings.search;
-
-import android.content.Context;
-import com.android.settings.search.DatabaseResultLoader;
-import com.android.settings.search.SearchResult;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Mock loader to subvert the requirements of returning data while also driving the Loader
- * lifecycle.
- */
-class MockDBLoader extends DatabaseResultLoader {
-
- public MockDBLoader(Context context) {
- super(context, "test", null);
- }
-
- @Override
- public Set<? extends SearchResult> loadInBackground() {
- return new HashSet<>();
- }
-
- @Override
- protected void onDiscardResult(Set<? extends SearchResult> result) {
-
- }
-}
diff --git a/tests/robotests/src/com/android/settings/search/MockInputDeviceResultLoader.java b/tests/robotests/src/com/android/settings/search/MockInputDeviceResultLoader.java
deleted file mode 100644
index 2c16b14..0000000
--- a/tests/robotests/src/com/android/settings/search/MockInputDeviceResultLoader.java
+++ /dev/null
@@ -1,38 +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.settings.search;
-
-import android.content.Context;
-
-import java.util.HashSet;
-import java.util.Set;
-
-public class MockInputDeviceResultLoader extends InputDeviceResultLoader {
- public MockInputDeviceResultLoader(Context context) {
- super(context, "test_query", null);
- }
-
- @Override
- public Set<? extends SearchResult> loadInBackground() {
- return new HashSet<>();
- }
-
- @Override
- protected void onDiscardResult(Set<? extends SearchResult> result) {
-
- }
-}
diff --git a/tests/robotests/src/com/android/settings/search/MockSearchResultLoader.java b/tests/robotests/src/com/android/settings/search/MockSearchResultLoader.java
new file mode 100644
index 0000000..6af258a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/search/MockSearchResultLoader.java
@@ -0,0 +1,29 @@
+package com.android.settings.search;
+
+import android.content.Context;
+
+import com.android.settings.search.SearchResult;
+import com.android.settings.search.SearchResultLoader;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Mock loader to subvert the requirements of returning data while also driving the Loader
+ * lifecycle.
+ */
+public class MockSearchResultLoader extends SearchResultLoader {
+
+ public MockSearchResultLoader(Context context) {
+ super(context, "test");
+ }
+
+ @Override
+ public List<? extends SearchResult> loadInBackground() {
+ return new ArrayList<>();
+ }
+
+ @Override
+ protected void onDiscardResult(List<? extends SearchResult> result) {
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java
index 050d7aa..a529b0b 100644
--- a/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java
@@ -21,7 +21,6 @@
import android.app.Activity;
import android.content.ComponentName;
-
import com.android.settings.TestConfig;
import com.android.settings.dashboard.SiteMapManager;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -33,6 +32,10 @@
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class SearchFeatureProviderImplTest {
@@ -43,7 +46,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mActivity = Robolectric.buildActivity(Activity.class).create().visible().get();
- mProvider = new SearchFeatureProviderImpl();
+ mProvider = spy(new SearchFeatureProviderImpl());
}
@Test
@@ -57,18 +60,19 @@
@Test
public void getDatabaseSearchLoader_shouldCleanupQuery() {
final String query = " space ";
- final DatabaseResultLoader loader = mProvider.getDatabaseSearchLoader(mActivity, query);
- assertThat(loader.mQueryText).isEqualTo(query.trim());
+ mProvider.getStaticSearchResultTask(mActivity, query);
+
+ verify(mProvider).cleanQuery(eq(query));
}
@Test
public void getInstalledAppSearchLoader_shouldCleanupQuery() {
final String query = " space ";
- final InstalledAppResultLoader loader =
- mProvider.getInstalledAppSearchLoader(mActivity, query);
- assertThat(loader.mQuery).isEqualTo(query.trim());
+ mProvider.getInstalledAppSearchTask(mActivity, query);
+
+ verify(mProvider).cleanQuery(eq(query));
}
@Test(expected = IllegalArgumentException.class)
@@ -87,4 +91,12 @@
final ComponentName cn = new ComponentName(mActivity.getPackageName(), "class");
mProvider.verifyLaunchSearchResultPageCaller(mActivity, cn);
}
+
+ @Test
+ public void cleanQuery_trimsWhitespace() {
+ final String query = " space ";
+ final String cleanQuery = "space";
+
+ assertThat(mProvider.cleanQuery(query)).isEqualTo(cleanQuery);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/search/SearchFragmentTest.java b/tests/robotests/src/com/android/settings/search/SearchFragmentTest.java
index b897008..a3f3334 100644
--- a/tests/robotests/src/com/android/settings/search/SearchFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchFragmentTest.java
@@ -18,6 +18,7 @@
package com.android.settings.search;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
@@ -54,9 +55,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
@@ -65,7 +64,7 @@
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
-import java.util.Set;
+import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH,
@@ -79,22 +78,13 @@
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock
- private DatabaseResultLoader mDatabaseResultLoader;
- @Mock
- private InstalledAppResultLoader mInstalledAppResultLoader;
- @Mock
- private AccessibilityServiceResultLoader mAccessibilityServiceResultLoader;
- @Mock
- private InputDeviceResultLoader mInputDeviceResultLoader;
-
+ private SearchResultLoader mSearchResultLoader;
@Mock
private SavedQueryLoader mSavedQueryLoader;
@Mock
private SavedQueryController mSavedQueryController;
@Mock
private SearchResultsAdapter mSearchResultsAdapter;
- @Captor
- private ArgumentCaptor<String> mQueryCaptor = ArgumentCaptor.forClass(String.class);
private FakeFeatureFactory mFeatureFactory;
@@ -113,17 +103,8 @@
@Test
public void screenRotate_shouldPersistQuery() {
when(mFeatureFactory.searchFeatureProvider
- .getDatabaseSearchLoader(any(Context.class), anyString()))
- .thenReturn(mDatabaseResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getInstalledAppSearchLoader(any(Context.class), anyString()))
- .thenReturn(mInstalledAppResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getAccessibilityServiceResultLoader(any(Context.class), anyString()))
- .thenReturn(mAccessibilityServiceResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getInputDeviceResultLoader(any(Context.class), anyString()))
- .thenReturn(mInputDeviceResultLoader);
+ .getSearchResultLoader(any(Context.class), anyString()))
+ .thenReturn(new MockSearchResultLoader(RuntimeEnvironment.application));
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
.thenReturn(mSavedQueryLoader);
@@ -168,25 +149,16 @@
activityController.setup(bundle);
verify(mFeatureFactory.searchFeatureProvider, never())
- .getDatabaseSearchLoader(any(Context.class), anyString());
+ .getStaticSearchResultTask(any(Context.class), anyString());
verify(mFeatureFactory.searchFeatureProvider, never())
- .getInstalledAppSearchLoader(any(Context.class), anyString());
+ .getInstalledAppSearchTask(any(Context.class), anyString());
}
@Test
- public void queryTextChange_shouldTriggerLoaderAndInitializeSearch() {
+ public void queryTextChange_shouldTriggerLoader() {
when(mFeatureFactory.searchFeatureProvider
- .getDatabaseSearchLoader(any(Context.class), anyString()))
- .thenReturn(mDatabaseResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getInstalledAppSearchLoader(any(Context.class), anyString()))
- .thenReturn(mInstalledAppResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getAccessibilityServiceResultLoader(any(Context.class), anyString()))
- .thenReturn(mAccessibilityServiceResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getInputDeviceResultLoader(any(Context.class), anyString()))
- .thenReturn(mInputDeviceResultLoader);
+ .getSearchResultLoader(any(Context.class), anyString()))
+ .thenReturn(mSearchResultLoader);
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
.thenReturn(mSavedQueryLoader);
@@ -199,7 +171,6 @@
when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class)))
.thenReturn(true);
- ReflectionHelpers.setField(fragment, "mSearchAdapter", mSearchResultsAdapter);
fragment.onQueryTextChange(testQuery);
activityController.get().onBackPressed();
@@ -209,11 +180,7 @@
any(Context.class),
eq(MetricsProto.MetricsEvent.ACTION_LEAVE_SEARCH_RESULT_WITHOUT_QUERY));
verify(mFeatureFactory.searchFeatureProvider)
- .getDatabaseSearchLoader(any(Context.class), anyString());
- verify(mFeatureFactory.searchFeatureProvider)
- .getInstalledAppSearchLoader(any(Context.class), anyString());
- verify(mSearchResultsAdapter).initializeSearch(mQueryCaptor.capture());
- assertThat(mQueryCaptor.getValue()).isEqualTo(testQuery);
+ .getSearchResultLoader(any(Context.class), anyString());
}
@Test
@@ -238,18 +205,6 @@
@Test
public void queryTextChangeToEmpty_shouldLoadSavedQueryAndNotInitializeSearch() {
- when(mFeatureFactory.searchFeatureProvider
- .getDatabaseSearchLoader(any(Context.class), anyString()))
- .thenReturn(mDatabaseResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getInstalledAppSearchLoader(any(Context.class), anyString()))
- .thenReturn(mInstalledAppResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getAccessibilityServiceResultLoader(any(Context.class), anyString()))
- .thenReturn(mAccessibilityServiceResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getInputDeviceResultLoader(any(Context.class), anyString()))
- .thenReturn(mInputDeviceResultLoader);
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
.thenReturn(mSavedQueryLoader);
ActivityController<SearchActivity> activityController =
@@ -266,27 +221,14 @@
fragment.onQueryTextChange("");
verify(mFeatureFactory.searchFeatureProvider, never())
- .getDatabaseSearchLoader(any(Context.class), anyString());
+ .getStaticSearchResultTask(any(Context.class), anyString());
verify(mFeatureFactory.searchFeatureProvider, never())
- .getInstalledAppSearchLoader(any(Context.class), anyString());
+ .getInstalledAppSearchTask(any(Context.class), anyString());
verify(mSavedQueryController).loadSavedQueries();
- verify(mSearchResultsAdapter, never()).initializeSearch(anyString());
}
@Test
public void updateIndex_TriggerOnCreate() {
- when(mFeatureFactory.searchFeatureProvider
- .getDatabaseSearchLoader(any(Context.class), anyString()))
- .thenReturn(mDatabaseResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getInstalledAppSearchLoader(any(Context.class), anyString()))
- .thenReturn(mInstalledAppResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getAccessibilityServiceResultLoader(any(Context.class), anyString()))
- .thenReturn(mAccessibilityServiceResultLoader);
- when(mFeatureFactory.searchFeatureProvider
- .getInputDeviceResultLoader(any(Context.class), anyString()))
- .thenReturn(mInputDeviceResultLoader);
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
.thenReturn(mSavedQueryLoader);
@@ -304,40 +246,10 @@
}
@Test
- public void syncLoaders_MergeWhenAllLoadersDone() {
- when(mFeatureFactory.searchFeatureProvider
- .getDatabaseSearchLoader(any(Context.class), anyString()))
- .thenReturn(new MockDBLoader(RuntimeEnvironment.application));
- when(mFeatureFactory.searchFeatureProvider
- .getInstalledAppSearchLoader(any(Context.class), anyString()))
- .thenReturn(new MockAppLoader(RuntimeEnvironment.application));
- when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
- .thenReturn(mSavedQueryLoader);
-
- ActivityController<SearchActivity> activityController =
- Robolectric.buildActivity(SearchActivity.class);
- activityController.setup();
-
- SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager()
- .findFragmentById(R.id.main_content));
- when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class)))
- .thenReturn(true);
-
- fragment.onQueryTextChange("non-empty");
-
- Robolectric.flushForegroundThreadScheduler();
-
- verify(fragment, times(2)).onLoadFinished(any(Loader.class), any(Set.class));
- }
-
- @Test
public void whenNoQuery_HideFeedbackIsCalled() {
when(mFeatureFactory.searchFeatureProvider
- .getDatabaseSearchLoader(any(Context.class), anyString()))
- .thenReturn(new MockDBLoader(RuntimeEnvironment.application));
- when(mFeatureFactory.searchFeatureProvider
- .getInstalledAppSearchLoader(any(Context.class), anyString()))
- .thenReturn(new MockAppLoader(RuntimeEnvironment.application));
+ .getSearchResultLoader(any(Context.class), anyString()))
+ .thenReturn(new MockSearchResultLoader(RuntimeEnvironment.application));
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
.thenReturn(mSavedQueryLoader);
@@ -359,17 +271,8 @@
@Test
public void onLoadFinished_ShowsFeedback() {
when(mFeatureFactory.searchFeatureProvider
- .getDatabaseSearchLoader(any(Context.class), anyString()))
- .thenReturn(new MockDBLoader(RuntimeEnvironment.application));
- when(mFeatureFactory.searchFeatureProvider
- .getInstalledAppSearchLoader(any(Context.class), anyString()))
- .thenReturn(new MockAppLoader(RuntimeEnvironment.application));
- when(mFeatureFactory.searchFeatureProvider
- .getAccessibilityServiceResultLoader(any(Context.class), anyString()))
- .thenReturn(new MockAccessibilityLoader(RuntimeEnvironment.application));
- when(mFeatureFactory.searchFeatureProvider
- .getInputDeviceResultLoader(any(Context.class), anyString()))
- .thenReturn(new MockInputDeviceResultLoader(RuntimeEnvironment.application));
+ .getSearchResultLoader(any(Context.class), anyString()))
+ .thenReturn(new MockSearchResultLoader(RuntimeEnvironment.application));
when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
.thenReturn(mSavedQueryLoader);
ActivityController<SearchActivity> activityController =
@@ -413,9 +316,7 @@
fragment.onIndexingFinished();
- verify(loaderManager).initLoader(eq(SearchFragment.SearchLoaderId.DATABASE),
- eq(null), any(LoaderManager.LoaderCallbacks.class));
- verify(loaderManager).initLoader(eq(SearchFragment.SearchLoaderId.INSTALLED_APPS),
+ verify(loaderManager).initLoader(eq(SearchFragment.SearchLoaderId.SEARCH_RESULT),
eq(null), any(LoaderManager.LoaderCallbacks.class));
}
@@ -480,16 +381,13 @@
eq("test_setting"),
argThat(pairMatches(MetricsProto.MetricsEvent.FIELD_SETTINGS_SEARCH_RESULT_COUNT)),
argThat(pairMatches(MetricsProto.MetricsEvent.FIELD_SETTINGS_SEARCH_RESULT_RANK)),
- argThat(pairMatches(MetricsProto.MetricsEvent
- .FIELD_SETTINGS_SEARCH_RESULT_ASYNC_RANKING_STATE)),
argThat(pairMatches(MetricsProto.MetricsEvent.FIELD_SETTINGS_SEARCH_QUERY_LENGTH)));
-
verify(mFeatureFactory.searchFeatureProvider).searchResultClicked(nullable(Context.class),
nullable(String.class), eq(searchResult));
}
@Test
- public void onResume_shouldCallSearchRankingWarmupIfSmartSearchRankingEnabled(){
+ public void onResume_shouldCallSearchRankingWarmupIfSmartSearchRankingEnabled() {
when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(any(Context.class)))
.thenReturn(true);
@@ -504,7 +402,7 @@
}
@Test
- public void onResume_shouldNotCallSearchRankingWarmupIfSmartSearchRankingDisabled(){
+ public void onResume_shouldNotCallSearchRankingWarmupIfSmartSearchRankingDisabled() {
when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(any(Context.class)))
.thenReturn(false);
diff --git a/tests/robotests/src/com/android/settings/search/SearchResultAggregatorTest.java b/tests/robotests/src/com/android/settings/search/SearchResultAggregatorTest.java
new file mode 100644
index 0000000..286d7cf9
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/search/SearchResultAggregatorTest.java
@@ -0,0 +1,271 @@
+package com.android.settings.search;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class SearchResultAggregatorTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+
+ private FakeFeatureFactory mFeatureFactory;
+
+ private SearchResultAggregator mAggregator;
+
+ @Mock
+ private DatabaseResultLoader mStaticTask;
+ @Mock
+ private InstalledAppResultLoader mAppTask;
+ @Mock
+ private InputDeviceResultLoader mInputTask;
+ @Mock
+ private AccessibilityServiceResultLoader mMAccessibilityTask;
+ @Mock
+ private ExecutorService mService;
+
+
+ private String[] DB_TITLES = {"static_one", "static_two"};
+ private String[] INPUT_TITLES = {"input_one", "input_two"};
+ private String[] ACCESS_TITLES = {"access_one", "access_two"};
+ private String[] APP_TITLES = {"app_one", "app_two"};
+
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mAggregator = spy(SearchResultAggregator.getInstance());
+ FakeFeatureFactory.setupForTest(mContext);
+ mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
+
+ // Return mock loaders from feature provider
+ when(mFeatureFactory.searchFeatureProvider.getStaticSearchResultTask(any(Context.class),
+ anyString())).thenReturn(mStaticTask);
+ when(mFeatureFactory.searchFeatureProvider.getInstalledAppSearchTask(any(Context.class),
+ anyString())).thenReturn(mAppTask);
+ when(mFeatureFactory.searchFeatureProvider.getInputDeviceResultTask(any(Context.class),
+ anyString())).thenReturn(mInputTask);
+ when(mFeatureFactory.searchFeatureProvider.getAccessibilityServiceResultTask(
+ any(Context.class),
+ anyString())).thenReturn(mMAccessibilityTask);
+ when(mFeatureFactory.searchFeatureProvider.getExecutorService()).thenReturn(mService);
+
+ // Return fake data from the loaders
+ List<? extends SearchResult> dbResults = getDummyDbResults();
+ doReturn(dbResults).when(mStaticTask).get(anyLong(), any(TimeUnit.class));
+
+ List<? extends SearchResult> appResults = getDummyAppResults();
+ doReturn(appResults).when(mAppTask).get(anyLong(), any(TimeUnit.class));
+
+ List<? extends SearchResult> inputResults = getDummyInputDeviceResults();
+ doReturn(inputResults).when(mInputTask).get(anyLong(), any(TimeUnit.class));
+
+ List<? extends SearchResult> accessResults = getDummyAccessibilityResults();
+ doReturn(accessResults).when(mMAccessibilityTask).get(anyLong(), any(TimeUnit.class));
+ }
+
+ @Test
+ public void testStaticResults_mergedProperly() {
+ when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(mContext))
+ .thenReturn(false);
+
+ List<? extends SearchResult> results = mAggregator.fetchResults(mContext, "test");
+
+ assertThat(results).hasSize(8);
+ assertThat(results.get(0).title).isEqualTo(DB_TITLES[0]);
+ assertThat(results.get(1).title).isEqualTo(DB_TITLES[1]);
+ assertThat(results.get(2).title).isEqualTo(APP_TITLES[0]);
+ assertThat(results.get(3).title).isEqualTo(ACCESS_TITLES[0]);
+ assertThat(results.get(4).title).isEqualTo(INPUT_TITLES[0]);
+ assertThat(results.get(5).title).isEqualTo(APP_TITLES[1]);
+ assertThat(results.get(6).title).isEqualTo(ACCESS_TITLES[1]);
+ assertThat(results.get(7).title).isEqualTo(INPUT_TITLES[1]);
+ }
+
+ @Test
+ public void testStaticRanking_staticThrowsException_dbResultsAreMissing() throws Exception {
+ when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(mContext))
+ .thenReturn(false);
+ when(mStaticTask.get(anyLong(), any(TimeUnit.class))).thenThrow(new InterruptedException());
+
+ List<? extends SearchResult> results = mAggregator.fetchResults(mContext, "test");
+
+ assertThat(results).hasSize(6);
+ assertThat(results.get(0).title).isEqualTo(APP_TITLES[0]);
+ assertThat(results.get(1).title).isEqualTo(ACCESS_TITLES[0]);
+ assertThat(results.get(2).title).isEqualTo(INPUT_TITLES[0]);
+ assertThat(results.get(3).title).isEqualTo(APP_TITLES[1]);
+ assertThat(results.get(4).title).isEqualTo(ACCESS_TITLES[1]);
+ assertThat(results.get(5).title).isEqualTo(INPUT_TITLES[1]);
+ }
+
+ @Test
+ public void testStaticRanking_appsThrowException_appResultsAreMissing() throws Exception {
+ when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(mContext))
+ .thenReturn(false);
+ when(mAppTask.get(anyLong(), any(TimeUnit.class))).thenThrow(new InterruptedException());
+
+ List<? extends SearchResult> results = mAggregator.fetchResults(mContext, "test");
+
+ assertThat(results).hasSize(6);
+ assertThat(results.get(0).title).isEqualTo(DB_TITLES[0]);
+ assertThat(results.get(1).title).isEqualTo(DB_TITLES[1]);
+ assertThat(results.get(2).title).isEqualTo(ACCESS_TITLES[0]);
+ assertThat(results.get(3).title).isEqualTo(INPUT_TITLES[0]);
+ assertThat(results.get(4).title).isEqualTo(ACCESS_TITLES[1]);
+ assertThat(results.get(5).title).isEqualTo(INPUT_TITLES[1]);
+ }
+
+ @Test
+ public void testStaticRanking_inputThrowException_inputResultsAreMissing() throws Exception {
+ when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(mContext))
+ .thenReturn(false);
+ when(mInputTask.get(anyLong(), any(TimeUnit.class))).thenThrow(new InterruptedException());
+
+ List<? extends SearchResult> results = mAggregator.fetchResults(mContext, "test");
+
+ assertThat(results).hasSize(6);
+ assertThat(results.get(0).title).isEqualTo(DB_TITLES[0]);
+ assertThat(results.get(1).title).isEqualTo(DB_TITLES[1]);
+ assertThat(results.get(2).title).isEqualTo(APP_TITLES[0]);
+ assertThat(results.get(3).title).isEqualTo(ACCESS_TITLES[0]);
+ assertThat(results.get(4).title).isEqualTo(APP_TITLES[1]);
+ assertThat(results.get(5).title).isEqualTo(ACCESS_TITLES[1]);
+ }
+
+ @Test
+ public void testStaticRanking_accessThrowException_accessResultsAreMissing() throws Exception {
+ when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(mContext))
+ .thenReturn(false);
+ when(mMAccessibilityTask.get(anyLong(), any(TimeUnit.class))).thenThrow(
+ new InterruptedException());
+
+ List<? extends SearchResult> results = mAggregator.fetchResults(mContext, "test");
+
+ assertThat(results).hasSize(6);
+ assertThat(results.get(0).title).isEqualTo(DB_TITLES[0]);
+ assertThat(results.get(1).title).isEqualTo(DB_TITLES[1]);
+ assertThat(results.get(2).title).isEqualTo(APP_TITLES[0]);
+ assertThat(results.get(3).title).isEqualTo(INPUT_TITLES[0]);
+ assertThat(results.get(4).title).isEqualTo(APP_TITLES[1]);
+ assertThat(results.get(5).title).isEqualTo(INPUT_TITLES[1]);
+ }
+
+ @Test
+ public void testDynamicRanking_sortsWithDynamicRanking() {
+ when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(
+ true);
+
+ List<? extends SearchResult> results = mAggregator.fetchResults(mContext, "test");
+
+ assertThat(results).hasSize(8);
+ assertThat(results.get(0).title).isEqualTo(DB_TITLES[0]);
+ assertThat(results.get(1).title).isEqualTo(DB_TITLES[1]);
+ assertThat(results.get(2).title).isEqualTo(APP_TITLES[0]);
+ assertThat(results.get(3).title).isEqualTo(ACCESS_TITLES[0]);
+ assertThat(results.get(4).title).isEqualTo(INPUT_TITLES[0]);
+ assertThat(results.get(5).title).isEqualTo(APP_TITLES[1]);
+ assertThat(results.get(6).title).isEqualTo(ACCESS_TITLES[1]);
+ assertThat(results.get(7).title).isEqualTo(INPUT_TITLES[1]);
+ }
+
+ private List<? extends SearchResult> getDummyDbResults() {
+ List<SearchResult> results = new ArrayList<>();
+ ResultPayload payload = new ResultPayload(new Intent());
+ SearchResult.Builder builder = new SearchResult.Builder();
+ builder.setPayload(payload)
+ .setTitle(DB_TITLES[0])
+ .setRank(1)
+ .setStableId(Objects.hash(DB_TITLES[0], "db"));
+ results.add(builder.build());
+
+ builder.setTitle(DB_TITLES[1])
+ .setRank(2)
+ .setStableId(Objects.hash(DB_TITLES[1], "db"));
+ results.add(builder.build());
+
+ return results;
+ }
+
+ private List<? extends SearchResult> getDummyAppResults() {
+ List<AppSearchResult> results = new ArrayList<>();
+ ResultPayload payload = new ResultPayload(new Intent());
+ AppSearchResult.Builder builder = new AppSearchResult.Builder();
+ builder.setPayload(payload)
+ .setTitle(APP_TITLES[0])
+ .setRank(1)
+ .setStableId(Objects.hash(APP_TITLES[0], "app"));
+ results.add(builder.build());
+
+ builder.setTitle(APP_TITLES[1])
+ .setRank(2)
+ .setStableId(Objects.hash(APP_TITLES[1], "app"));
+ results.add(builder.build());
+
+ return results;
+ }
+
+ public List<? extends SearchResult> getDummyInputDeviceResults() {
+ List<SearchResult> results = new ArrayList<>();
+ ResultPayload payload = new ResultPayload(new Intent());
+ AppSearchResult.Builder builder = new AppSearchResult.Builder();
+ builder.setPayload(payload)
+ .setTitle(INPUT_TITLES[0])
+ .setRank(1)
+ .setStableId(Objects.hash(INPUT_TITLES[0], "app"));
+ results.add(builder.build());
+
+ builder.setTitle(INPUT_TITLES[1])
+ .setRank(2)
+ .setStableId(Objects.hash(INPUT_TITLES[1], "app"));
+ results.add(builder.build());
+
+ return results;
+ }
+
+ public List<? extends SearchResult> getDummyAccessibilityResults() {
+ List<SearchResult> results = new ArrayList<>();
+ ResultPayload payload = new ResultPayload(new Intent());
+ AppSearchResult.Builder builder = new AppSearchResult.Builder();
+ builder.setPayload(payload)
+ .setTitle(ACCESS_TITLES[0])
+ .setRank(1)
+ .setStableId(Objects.hash(ACCESS_TITLES[0], "app"));
+ results.add(builder.build());
+
+ builder.setTitle(ACCESS_TITLES[1])
+ .setRank(2)
+ .setStableId(Objects.hash(ACCESS_TITLES[1], "app"));
+ results.add(builder.build());
+
+ return results;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java b/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java
index a2afb35..4baf8d2 100644
--- a/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java
@@ -18,44 +18,32 @@
package com.android.settings.search;
import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import android.util.Pair;
import android.view.ViewGroup;
import android.widget.FrameLayout;
-import com.android.settings.R;
import com.android.settings.TestConfig;
-import com.android.settings.search.SearchResult.Builder;
-import com.android.settings.search.ranking.SearchResultsRankerCallback;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowLooper;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -67,25 +55,18 @@
private SearchFeatureProvider mSearchFeatureProvider;
@Mock
private Context mMockContext;
- @Captor
- private ArgumentCaptor<Integer> mSearchResultsCountCaptor =
- ArgumentCaptor.forClass(Integer.class);
private SearchResultsAdapter mAdapter;
private Context mContext;
- private String mLoaderClassName;
-
- private String[] TITLES = {"alpha", "bravo", "charlie", "appAlpha", "appBravo", "appCharlie"};
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = Robolectric.buildActivity(Activity.class).get();
- mLoaderClassName = DatabaseResultLoader.class.getName();
when(mFragment.getContext()).thenReturn(mMockContext);
when(mMockContext.getApplicationContext()).thenReturn(mContext);
when(mSearchFeatureProvider.smartSearchRankingTimeoutMs(any(Context.class)))
.thenReturn(300L);
- mAdapter = new SearchResultsAdapter(mFragment, mSearchFeatureProvider);
+ mAdapter = new SearchResultsAdapter(mFragment);
}
@Test
@@ -95,17 +76,6 @@
}
@Test
- public void testSingleSourceMerge_exactCopyReturned() {
- Set<SearchResult> intentResults = getIntentSampleResults();
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(intentResults, mLoaderClassName);
- mAdapter.notifyResultsLoaded();
-
- List<SearchResult> updatedResults = mAdapter.getSearchResults();
- assertThat(updatedResults).containsAllIn(intentResults);
- }
-
- @Test
public void testCreateViewHolder_returnsIntentResult() {
ViewGroup group = new FrameLayout(mContext);
SearchViewHolder view = mAdapter.onCreateViewHolder(group,
@@ -123,387 +93,13 @@
}
@Test
- public void testEndToEndSearch_properResultsMerged_correctOrder() {
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(new HashSet<>(getDummyAppResults()),
- InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(new HashSet<>(getDummyDbResults()),
- DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
+ public void testPostSearchResults_addsDataAndDisplays() {
+ List<SearchResult> results = getDummyDbResults();
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
- assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
- verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
- }
+ mAdapter.postSearchResults(results);
- @Test
- public void testEndToEndSearch_addResults_resultsAddedInOrder() {
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- // Add two individual items
- mAdapter.addSearchResults(new HashSet<>(appResults.subList(0, 1)),
- InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(new HashSet<>(dbResults.subList(0, 1)),
- DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
- // Add super-set of items
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
-
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
- assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
- verify(mFragment, times(2)).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getAllValues().toArray())
- .isEqualTo(new Integer[] {2, 6});
- }
-
- @Test
- public void testEndToEndSearch_removeResults_resultsAdded() {
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- // Add list of items
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(new HashSet<>(appResults),
- InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(new HashSet<>(dbResults),
- DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
- // Add subset of items
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(new HashSet<>(appResults.subList(0, 1)),
- InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(new HashSet<>(dbResults.subList(0, 1)),
- DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
-
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[0]);
- assertThat(results.get(1).title).isEqualTo(TITLES[3]);
- verify(mFragment, times(2)).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getAllValues().toArray())
- .isEqualTo(new Integer[] {6, 2});
- }
- @Test
- public void testEndToEndSearch_smartSearchRankingEnabledAndSucceededAfterResultsLoaded() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
- mAdapter.onRankingScoresAvailable(getDummyRankingScores());
-
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[2]); // charlie
- assertThat(results.get(1).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(2).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(3).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(4).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(5).title).isEqualTo(TITLES[5]); // appCharlie
- verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
- assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.SUCCEEDED);
- }
-
- @Test
- public void testEndToEndSearch_smartSearchRankingEnabledAndSucceededBeforeResultsLoaded() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.onRankingScoresAvailable(getDummyRankingScores());
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
-
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[2]); // charlie
- assertThat(results.get(1).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(2).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(3).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(4).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(5).title).isEqualTo(TITLES[5]); // appCharlie
- verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
- assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.SUCCEEDED);
- }
-
- @Test
- public void testEndToEndSearch_smartSearchRankingEnabledAndFailedAfterResultsLoaded() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
- mAdapter.onRankingFailed();
-
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
- assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
- verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
- assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.FAILED);
- }
-
- @Test
- public void testEndToEndSearch_smartSearchRankingEnabledAndFailedBeforeResultsLoaded() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.onRankingFailed();
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
-
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
- assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
- verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
- assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.FAILED);
- }
-
- @Test
- public void testEndToEndSearch_smartSearchRankingEnabledAndTimedoutAfterResultsLoaded() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
-
- waitUntilRankingTimesOut();
-
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
- assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
- verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
- assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.TIMED_OUT);
- }
-
- @Test
- public void testEndToEndSearch_smartSearchRankingEnabledAndTimedoutBeforeResultsLoaded() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
-
- waitUntilRankingTimesOut();
-
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
-
- List<SearchResult> results = mAdapter.getSearchResults();
- assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(1).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(2).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(3).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(4).title).isEqualTo(TITLES[5]); // appCharlie
- assertThat(results.get(5).title).isEqualTo(TITLES[2]); // charlie
- verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
- assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
- assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.TIMED_OUT);
- }
-
- @Test
- public void testDoSmartRanking_shouldRankAppResultsAfterDbResults() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
- mAdapter.onRankingScoresAvailable(getDummyRankingScores());
- List<SearchResult> results = mAdapter.doAsyncRanking();
- assertThat(results.get(0).title).isEqualTo(TITLES[2]); // charlie
- assertThat(results.get(1).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(2).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(3).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(4).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(5).title).isEqualTo(TITLES[5]); // appCharlie
- }
-
- @Test
- public void testDoSmartRanking_shouldRankResultsWithMissingScoresAfterScoredResults() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- mAdapter.notifyResultsLoaded();
- List<Pair<String, Float>> rankingScores = getDummyRankingScores();
- rankingScores.remove(1); // no ranking score for alpha
- mAdapter.onRankingScoresAvailable(rankingScores);
- List<SearchResult> results = mAdapter.doAsyncRanking();
- assertThat(results.get(0).title).isEqualTo(TITLES[2]); // charlie
- assertThat(results.get(1).title).isEqualTo(TITLES[1]); // bravo
- assertThat(results.get(2).title).isEqualTo(TITLES[0]); // alpha
- assertThat(results.get(3).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(results.get(4).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(results.get(5).title).isEqualTo(TITLES[5]); // appCharlie
- }
-
- @Test
- public void testGetUnsortedLoadedResults () {
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- Set<CharSequence> expectedDbTitles = new HashSet<>(
- Arrays.asList("alpha", "bravo", "charlie"));
- Set<CharSequence> expectedAppTitles = new HashSet<>(
- Arrays.asList("appAlpha", "appBravo", "appCharlie"));
- Set<CharSequence> actualDbTitles = new HashSet<>();
- Set<CharSequence> actualAppTitles = new HashSet<>();
- for (SearchResult result : mAdapter.getUnsortedLoadedResults(SearchResultsAdapter
- .DB_RESULTS_LOADER_KEY)) {
- actualDbTitles.add(result.title);
- }
- for (SearchResult result : mAdapter.getUnsortedLoadedResults(SearchResultsAdapter
- .APP_RESULTS_LOADER_KEY)) {
- actualAppTitles.add(result.title);
- }
- assertThat(actualDbTitles).isEqualTo(expectedDbTitles);
- assertThat(actualAppTitles).isEqualTo(expectedAppTitles);
- }
-
- @Test
- public void testGetSortedLoadedResults() {
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.addSearchResults(
- new HashSet<>(appResults), InstalledAppResultLoader.class.getName());
- mAdapter.addSearchResults(
- new HashSet<>(dbResults), DatabaseResultLoader.class.getName());
- List<? extends SearchResult> actualDbResults =
- mAdapter.getSortedLoadedResults(SearchResultsAdapter.DB_RESULTS_LOADER_KEY);
- List<? extends SearchResult> actualAppResults =
- mAdapter.getSortedLoadedResults(SearchResultsAdapter.APP_RESULTS_LOADER_KEY);
- assertThat(actualDbResults.get(0).title).isEqualTo(TITLES[0]); // charlie
- assertThat(actualDbResults.get(1).title).isEqualTo(TITLES[1]); // bravo
- assertThat(actualDbResults.get(2).title).isEqualTo(TITLES[2]); // alpha
- assertThat(actualAppResults.get(0).title).isEqualTo(TITLES[3]); // appAlpha
- assertThat(actualAppResults.get(1).title).isEqualTo(TITLES[4]); // appBravo
- assertThat(actualAppResults.get(2).title).isEqualTo(TITLES[5]); // appCharlie
- }
-
- @Test
- public void testInitializeSearch_shouldNotRunSmartRankingIfDisabled() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(false);
- mAdapter.initializeSearch("");
- mAdapter.notifyResultsLoaded();
- verify(mSearchFeatureProvider, never()).querySearchResults(
- any(Context.class), anyString(), any(SearchResultsRankerCallback.class));
- assertThat(mAdapter.getAsyncRankingState()).isEqualTo(SearchResultsAdapter.DISABLED);
- }
-
- @Test
- public void testInitialSearch_shouldRunSmartRankingIfEnabled() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
- mAdapter.initializeSearch("");
- mAdapter.notifyResultsLoaded();
- verify(mSearchFeatureProvider, times(1)).querySearchResults(
- any(Context.class), anyString(), any(SearchResultsRankerCallback.class));
- assertThat(mAdapter.getAsyncRankingState())
- .isEqualTo(SearchResultsAdapter.PENDING_RESULTS);
- }
-
- @Test
- public void testGetRankingScoreByStableId() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(true);
-
- List<SearchResult> appResults = getDummyAppResults();
- List<SearchResult> dbResults = getDummyDbResults();
- mAdapter.initializeSearch("");
- mAdapter.onRankingScoresAvailable(getDummyRankingScores());
- assertThat(mAdapter.getRankingScoreByStableId(dbResults.get(0).stableId))
- .isWithin(1e-10f).of(0.8f);
- assertThat(mAdapter.getRankingScoreByStableId(dbResults.get(1).stableId))
- .isWithin(1e-10f).of(0.2f);
- assertThat(mAdapter.getRankingScoreByStableId(dbResults.get(2).stableId))
- .isWithin(1e-10f).of(0.9f);
- assertThat(mAdapter.getRankingScoreByStableId(appResults.get(0).stableId))
- .isEqualTo(-Float.MAX_VALUE);
- assertThat(mAdapter.getRankingScoreByStableId(appResults.get(1).stableId))
- .isEqualTo(-Float.MAX_VALUE);
- assertThat(mAdapter.getRankingScoreByStableId(appResults.get(2).stableId))
- .isEqualTo(-Float.MAX_VALUE);
- }
-
- private void waitUntilRankingTimesOut() {
- while (mAdapter.getHandler().hasMessages(mAdapter.MSG_RANKING_TIMED_OUT)) {
- try {
- ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
- Thread.sleep(100);
- } catch (InterruptedException e) {
- // Do nothing
- }
- }
+ assertThat(mAdapter.getSearchResults()).containsExactlyElementsIn(results);
+ verify(mFragment).onSearchResultsDisplayed(anyInt());
}
private List<SearchResult> getDummyDbResults() {
@@ -511,78 +107,21 @@
ResultPayload payload = new ResultPayload(new Intent());
SearchResult.Builder builder = new SearchResult.Builder();
builder.setPayload(payload)
- .setTitle(TITLES[0])
+ .setTitle("one")
.setRank(1)
- .setStableId(Objects.hash(TITLES[0], "db"));
+ .setStableId(Objects.hash("one", "db"));
results.add(builder.build());
- builder.setTitle(TITLES[1])
+ builder.setTitle("two")
.setRank(3)
- .setStableId(Objects.hash(TITLES[1], "db"));
+ .setStableId(Objects.hash("two", "db"));
results.add(builder.build());
- builder.setTitle(TITLES[2])
+ builder.setTitle("three")
.setRank(6)
- .setStableId(Objects.hash(TITLES[2], "db"));
+ .setStableId(Objects.hash("three", "db"));
results.add(builder.build());
return results;
}
-
- private List<SearchResult> getDummyAppResults() {
- List<SearchResult> results = new ArrayList<>();
- ResultPayload payload = new ResultPayload(new Intent());
- AppSearchResult.Builder builder = new AppSearchResult.Builder();
- builder.setPayload(payload)
- .setTitle(TITLES[3])
- .setRank(1)
- .setStableId(Objects.hash(TITLES[3], "app"));
- results.add(builder.build());
-
- builder.setTitle(TITLES[4])
- .setRank(2)
- .setStableId(Objects.hash(TITLES[4], "app"));
- results.add(builder.build());
-
- builder.setTitle(TITLES[5])
- .setRank(4)
- .setStableId(Objects.hash(TITLES[5], "app"));
- results.add(builder.build());
-
- return results;
- }
-
- private Set<SearchResult> getIntentSampleResults() {
- Set<SearchResult> sampleResults = new HashSet<>();
- ArrayList<String> breadcrumbs = new ArrayList<>();
- final Drawable icon = mContext.getDrawable(R.drawable.ic_search_24dp);
- final ResultPayload payload = new ResultPayload(null);
- final SearchResult.Builder builder = new Builder();
- builder.setTitle("title")
- .setSummary("summary")
- .setRank(1)
- .addBreadcrumbs(breadcrumbs)
- .setIcon(icon)
- .setPayload(payload)
- .setStableId(Objects.hash("title", "summary", 1));
- sampleResults.add(builder.build());
-
- builder.setRank(2)
- .setStableId(Objects.hash("title", "summary", 2));
- sampleResults.add(builder.build());
-
- builder.setRank(3)
- .setStableId(Objects.hash("title", "summary", 3));
- sampleResults.add(builder.build());
- return sampleResults;
- }
-
- private List<Pair<String, Float>> getDummyRankingScores() {
- List<SearchResult> results = getDummyDbResults();
- List<Pair<String, Float>> scores = new ArrayList<>();
- scores.add(new Pair<>(Long.toString(results.get(2).stableId), 0.9f)); // charlie
- scores.add(new Pair<>(Long.toString(results.get(0).stableId), 0.8f)); // alpha
- scores.add(new Pair<>(Long.toString(results.get(1).stableId), 0.2f)); // bravo
- return scores;
- }
}
diff --git a/tests/robotests/src/com/android/settings/search/DatabaseResultLoaderTest.java b/tests/robotests/src/com/android/settings/search/StaticSearchResultFutureTaskTest.java
similarity index 61%
rename from tests/robotests/src/com/android/settings/search/DatabaseResultLoaderTest.java
rename to tests/robotests/src/com/android/settings/search/StaticSearchResultFutureTaskTest.java
index dd7b7a9..e285555 100644
--- a/tests/robotests/src/com/android/settings/search/DatabaseResultLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/search/StaticSearchResultFutureTaskTest.java
@@ -21,9 +21,11 @@
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
+import android.util.Pair;
import com.android.settings.TestConfig;
import com.android.settings.dashboard.SiteMapManager;
+import com.android.settings.search.DatabaseResultLoader.StaticSearchResultCallable;
import com.android.settings.search.indexing.IndexData;
import com.android.settings.testutils.DatabaseTestUtils;
import com.android.settings.testutils.FakeFeatureFactory;
@@ -39,37 +41,54 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class DatabaseResultLoaderTest {
+public class StaticSearchResultFutureTaskTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mMockContext;
@Mock
private SiteMapManager mSiteMapManager;
+ @Mock
+ private ExecutorService mService;
private Context mContext;
SQLiteDatabase mDb;
+ FakeFeatureFactory mFeatureFactory;
+
+ private final String[] STATIC_TITLES = {"static one", "static two", "static three"};
+ private final int[] STABLE_IDS =
+ {"id_one".hashCode(), "id_two".hashCode(), "id_three".hashCode()};
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
- FakeFeatureFactory.setupForTest(mMockContext);
- FakeFeatureFactory factory =
- (FakeFeatureFactory) FakeFeatureFactory.getFactory(mMockContext);
- when(factory.searchFeatureProvider.getSiteMapManager())
+ mFeatureFactory = FakeFeatureFactory.setupForTest(mMockContext);
+ when(mFeatureFactory.searchFeatureProvider.getExecutorService()).thenReturn(mService);
+ when(mFeatureFactory.searchFeatureProvider.getSiteMapManager())
.thenReturn(mSiteMapManager);
mDb = IndexDatabaseHelper.getInstance(mContext).getWritableDatabase();
setUpDb();
@@ -81,159 +100,252 @@
}
@Test
- public void testMatchTitle() {
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "title", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(2);
+ public void testMatchTitle() throws Exception {
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "title",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(2);
verify(mSiteMapManager, times(2)).buildBreadCrumb(eq(mContext), anyString(), anyString());
}
@Test
- public void testMatchSummary() {
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "summary",
+ public void testMatchSummary() throws Exception {
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "summary",
mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(2);
+
+ assertThat(loader.call()).hasSize(2);
}
@Test
- public void testMatchKeywords() {
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "keywords",
+ public void testMatchKeywords() throws Exception {
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "keywords",
mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(2);
+
+ assertThat(loader.call()).hasSize(2);
}
@Test
- public void testMatchEntries() {
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "entries",
+ public void testMatchEntries() throws Exception {
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "entries",
mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(2);
+
+ assertThat(loader.call()).hasSize(2);
}
@Test
- public void testSpecialCaseWord_matchesNonPrefix() {
+ public void testSpecialCaseWord_matchesNonPrefix() throws Exception {
insertSpecialCase("Data usage");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "usage", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "usage",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCaseDash_matchesWordNoDash() {
+ public void testSpecialCaseDash_matchesWordNoDash() throws Exception {
insertSpecialCase("wi-fi calling");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "wifi", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
- }
- @Test
- public void testSpecialCaseDash_matchesWordWithDash() {
- insertSpecialCase("priorités seulment");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "priorités",
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "wifi",
mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCaseDash_matchesWordWithoutDash() {
+ public void testSpecialCaseDash_matchesWordWithDash() throws Exception {
insertSpecialCase("priorités seulment");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "priorites",
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "priorités",
mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCaseDash_matchesEntireQueryWithoutDash() {
+ public void testSpecialCaseDash_matchesWordWithoutDash() throws Exception {
+ insertSpecialCase("priorités seulment");
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "priorites",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(1);
+ }
+
+ @Test
+ public void testSpecialCaseDash_matchesEntireQueryWithoutDash() throws Exception {
insertSpecialCase("wi-fi calling");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "wifi calling",
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "wifi calling",
mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCasePrefix_matchesPrefixOfEntry() {
+ public void testSpecialCasePrefix_matchesPrefixOfEntry() throws Exception {
insertSpecialCase("Photos");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "pho", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "pho",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCasePrefix_DoesNotMatchNonPrefixSubstring() {
+ public void testSpecialCasePrefix_DoesNotMatchNonPrefixSubstring() throws Exception {
insertSpecialCase("Photos");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "hot", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(0);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "hot",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(0);
}
@Test
- public void testSpecialCaseMultiWordPrefix_matchesPrefixOfEntry() {
+ public void testSpecialCaseMultiWordPrefix_matchesPrefixOfEntry() throws Exception {
insertSpecialCase("Apps Notifications");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "Apps", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "Apps",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCaseMultiWordPrefix_matchesSecondWordPrefixOfEntry() {
+ public void testSpecialCaseMultiWordPrefix_matchesSecondWordPrefixOfEntry() throws Exception {
insertSpecialCase("Apps Notifications");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "Not", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "Not",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCaseMultiWordPrefix_DoesNotMatchMatchesPrefixOfFirstEntry() {
+ public void testSpecialCaseMultiWordPrefix_DoesNotMatchMatchesPrefixOfFirstEntry()
+ throws Exception {
insertSpecialCase("Apps Notifications");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "pp", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(0);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "pp",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(0);
}
@Test
- public void testSpecialCaseMultiWordPrefix_DoesNotMatchMatchesPrefixOfSecondEntry() {
+ public void testSpecialCaseMultiWordPrefix_DoesNotMatchMatchesPrefixOfSecondEntry()
+ throws Exception {
insertSpecialCase("Apps Notifications");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "tion", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(0);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "tion",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(0);
}
@Test
- public void testSpecialCaseMultiWordPrefixWithSpecial_matchesPrefixOfEntry() {
+ public void testSpecialCaseMultiWordPrefixWithSpecial_matchesPrefixOfEntry() throws
+ Exception {
insertSpecialCase("Apps & Notifications");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "App", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "App",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCaseMultiWordPrefixWithSpecial_matchesPrefixOfSecondEntry() {
+ public void testSpecialCaseMultiWordPrefixWithSpecial_matchesPrefixOfSecondEntry()
+ throws Exception {
insertSpecialCase("Apps & Notifications");
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "No", mSiteMapManager);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "No",
+ mSiteMapManager);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testResultMatchedByMultipleQueries_duplicatesRemoved() {
+ public void testResultMatchedByMultipleQueries_duplicatesRemoved() throws Exception {
String key = "durr";
insertSameValueAllFieldsCase(key);
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, key, null);
- assertThat(loader.loadInBackground().size()).isEqualTo(1);
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, key, null);
+
+ assertThat(loader.call()).hasSize(1);
}
@Test
- public void testSpecialCaseTwoWords_multipleResults() {
+ public void testSpecialCaseTwoWords_multipleResults() throws Exception {
final String caseOne = "Apple pear";
final String caseTwo = "Banana apple";
insertSpecialCase(caseOne);
insertSpecialCase(caseTwo);
- DatabaseResultLoader loader = new DatabaseResultLoader(mContext, "App", null);
- Set<? extends SearchResult> results = loader.loadInBackground();
- Set<CharSequence> expectedTitles = new HashSet<>(Arrays.asList(caseOne, caseTwo));
- Set<CharSequence> actualTitles = new HashSet<>();
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "App", null);
+
+ List<? extends SearchResult> results = loader.call();
+
+ Set<String> actualTitles = new HashSet<>();
for (SearchResult result : results) {
- actualTitles.add(result.title);
+ actualTitles.add(result.title.toString());
}
- assertThat(actualTitles).isEqualTo(expectedTitles);
+ assertThat(actualTitles).containsAllOf(caseOne, caseTwo);
+ }
+
+ @Test
+ public void testGetRankingScoreByStableId_sortedDynamically() throws Exception {
+ FutureTask<List<Pair<String, Float>>> task = mock(FutureTask.class);
+ when(task.get(anyLong(), any(TimeUnit.class))).thenReturn(getDummyRankingScores());
+ when(mFeatureFactory.searchFeatureProvider.getRankerTask(any(Context.class),
+ anyString())).thenReturn(task);
+ when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(
+ true);
+
+ insertSpecialCase(STATIC_TITLES[0], STABLE_IDS[0]);
+ insertSpecialCase(STATIC_TITLES[1], STABLE_IDS[1]);
+ insertSpecialCase(STATIC_TITLES[2], STABLE_IDS[2]);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "Static",
+ null);
+
+ List<? extends SearchResult> results = loader.call();
+
+ assertThat(results.get(0).title).isEqualTo(STATIC_TITLES[2]);
+ assertThat(results.get(1).title).isEqualTo(STATIC_TITLES[0]);
+ assertThat(results.get(2).title).isEqualTo(STATIC_TITLES[1]);
+ }
+
+ @Test
+ public void testGetRankingScoreByStableId_scoresTimeout_sortedStatically() throws Exception {
+ Callable<List<Pair<String, Float>>> callable = mock(Callable.class);
+ when(callable.call()).thenThrow(new TimeoutException());
+ FutureTask<List<Pair<String, Float>>> task = new FutureTask<>(callable);
+ when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(any())).thenReturn(
+ true);
+ when(mFeatureFactory.searchFeatureProvider.getRankerTask(any(Context.class),
+ anyString())).thenReturn(task);
+ insertSpecialCase("title", STABLE_IDS[0]);
+
+ StaticSearchResultCallable loader = new StaticSearchResultCallable(mContext, "title", null);
+
+ List<? extends SearchResult> results = loader.call();
+ assertThat(results.get(0).title).isEqualTo("title");
+ assertThat(results.get(1).title).isEqualTo("alpha_title");
+ assertThat(results.get(2).title).isEqualTo("bravo_title");
}
private void insertSpecialCase(String specialCase) {
+ insertSpecialCase(specialCase, specialCase.hashCode());
+ }
+
+ private void insertSpecialCase(String specialCase, int docId) {
String normalized = IndexData.normalizeHyphen(specialCase);
normalized = IndexData.normalizeString(normalized);
final ResultPayload payload = new ResultPayload(new Intent());
ContentValues values = new ContentValues();
- values.put(IndexDatabaseHelper.IndexColumns.DOCID, normalized.hashCode());
+ values.put(IndexDatabaseHelper.IndexColumns.DOCID, docId);
values.put(IndexDatabaseHelper.IndexColumns.LOCALE, "en-us");
values.put(IndexDatabaseHelper.IndexColumns.DATA_RANK, 1);
values.put(IndexDatabaseHelper.IndexColumns.DATA_TITLE, specialCase);
@@ -373,4 +485,33 @@
mDb.replaceOrThrow(IndexDatabaseHelper.Tables.TABLE_PREFS_INDEX, null, values);
}
+
+ private List<? extends SearchResult> getDummyDbResults() {
+ List<SearchResult> results = new ArrayList<>();
+ ResultPayload payload = new ResultPayload(new Intent());
+ SearchResult.Builder builder = new SearchResult.Builder();
+ builder.setPayload(payload)
+ .setTitle(STATIC_TITLES[0])
+ .setStableId(STABLE_IDS[0]);
+ results.add(builder.build());
+
+ builder.setTitle(STATIC_TITLES[1])
+ .setStableId(STABLE_IDS[1]);
+ results.add(builder.build());
+
+ builder.setTitle(STATIC_TITLES[2])
+ .setStableId(STABLE_IDS[2]);
+ results.add(builder.build());
+
+ return results;
+ }
+
+ private List<Pair<String, Float>> getDummyRankingScores() {
+ List<? extends SearchResult> results = getDummyDbResults();
+ List<Pair<String, Float>> scores = new ArrayList<>();
+ scores.add(new Pair<>(Long.toString(results.get(2).stableId), 0.9f)); // static_three
+ scores.add(new Pair<>(Long.toString(results.get(0).stableId), 0.8f)); // static_one
+ scores.add(new Pair<>(Long.toString(results.get(1).stableId), 0.2f)); // static_two
+ return scores;
+ }
}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/EncryptionAndCredentialTest.java b/tests/robotests/src/com/android/settings/security/EncryptionAndCredentialTest.java
similarity index 95%
rename from tests/robotests/src/com/android/settings/EncryptionAndCredentialTest.java
rename to tests/robotests/src/com/android/settings/security/EncryptionAndCredentialTest.java
index 7a3875e..c87c964 100644
--- a/tests/robotests/src/com/android/settings/EncryptionAndCredentialTest.java
+++ b/tests/robotests/src/com/android/settings/security/EncryptionAndCredentialTest.java
@@ -11,14 +11,14 @@
* 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
+ * limitations under the License.
*/
-package com.android.settings;
+package com.android.settings.security;
import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE;
import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE;
-import static com.android.settings.EncryptionAndCredential.SEARCH_INDEX_DATA_PROVIDER;
+import static com.android.settings.security.EncryptionAndCredential.SEARCH_INDEX_DATA_PROVIDER;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
@@ -28,6 +28,8 @@
import android.provider.SearchIndexableResource;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.TestConfig;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowVibrator.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowVibrator.java
new file mode 100644
index 0000000..9046720
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowVibrator.java
@@ -0,0 +1,56 @@
+/*
+ * 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.settings.testutils.shadow;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.os.SystemVibrator;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.fakes.RoboVibrator;
+import org.robolectric.shadows.ShadowContextImpl;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.Map;
+
+@Implements(SystemVibrator.class)
+public class ShadowVibrator {
+
+ private static Map<String, String> getSystemServiceMap() {
+ return ReflectionHelpers.getStaticField(ShadowContextImpl.class, "SYSTEM_SERVICE_MAP");
+ }
+
+ public static void addToServiceMap() {
+ getSystemServiceMap().put(Context.VIBRATOR_SERVICE, SystemVibrator.class.getName());
+ }
+
+ public static void reset() {
+ getSystemServiceMap().put(Context.VIBRATOR_SERVICE, RoboVibrator.class.getName());
+ }
+
+ public final Vibrator delegate = mock(Vibrator.class);
+
+ @Implementation
+ public void vibrate(int uid, String opPkg, VibrationEffect vibe, AudioAttributes attributes) {
+ delegate.vibrate(uid, opPkg, vibe, attributes);
+ }
+}
diff --git a/tests/unit/Android.mk b/tests/unit/Android.mk
index ec0f0b6..58fe7dd 100644
--- a/tests/unit/Android.mk
+++ b/tests/unit/Android.mk
@@ -10,13 +10,13 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
espresso-core \
- legacy-android-test \
- mockito-target-minus-junit4 \
- truth-prebuilt \
- ub-uiautomator \
espresso-contrib-nodep \
espresso-intents-nodep \
-
+ legacy-android-test \
+ mockito-target-minus-junit4 \
+ platform-test-annotations \
+ truth-prebuilt \
+ ub-uiautomator
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java b/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java
new file mode 100644
index 0000000..c75ca13
--- /dev/null
+++ b/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.settings.core;
+
+import static junit.framework.Assert.fail;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.provider.SearchIndexableResource;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.ArraySet;
+
+import com.android.settings.search.DatabaseIndexingUtils;
+import com.android.settings.search.Indexable;
+import com.android.settings.search.SearchIndexableResources;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class PreferenceControllerContractTest {
+
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ }
+
+ @Test
+ @Presubmit
+ public void controllersInSearchShouldImplementPreferenceControllerMixin() {
+ final Set<String> errorClasses = new ArraySet<>();
+
+ for (SearchIndexableResource page : SearchIndexableResources.values()) {
+ final Class<?> clazz = DatabaseIndexingUtils.getIndexableClass(page.className);
+
+ final Indexable.SearchIndexProvider provider =
+ DatabaseIndexingUtils.getSearchIndexProvider(clazz);
+ if (provider == null) {
+ continue;
+ }
+
+ final List<AbstractPreferenceController> controllers =
+ provider.getPreferenceControllers(mContext);
+ if (controllers == null) {
+ continue;
+ }
+ for (AbstractPreferenceController controller : controllers) {
+ if (!(controller instanceof PreferenceControllerMixin)) {
+ errorClasses.add(controller.getClass().getName());
+ }
+ }
+ }
+
+ if (!errorClasses.isEmpty()) {
+ final StringBuilder errorMessage = new StringBuilder()
+ .append("Each preference must implement PreferenceControllerMixin, ")
+ .append("the following classes don't:\n");
+ for (String c : errorClasses) {
+ errorMessage.append(c).append("\n");
+ }
+ fail(errorMessage.toString());
+ }
+ }
+}
diff --git a/tests/unit/src/com/android/settings/UniquePreferenceTest.java b/tests/unit/src/com/android/settings/core/UniquePreferenceTest.java
similarity index 98%
rename from tests/unit/src/com/android/settings/UniquePreferenceTest.java
rename to tests/unit/src/com/android/settings/core/UniquePreferenceTest.java
index 74f46eb..4c51772 100644
--- a/tests/unit/src/com/android/settings/UniquePreferenceTest.java
+++ b/tests/unit/src/com/android/settings/core/UniquePreferenceTest.java
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package com.android.settings;
+package com.android.settings.core;
import static junit.framework.Assert.fail;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import android.platform.test.annotations.Presubmit;
import android.provider.SearchIndexableResource;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
@@ -83,6 +84,7 @@
* should have a key.
*/
@Test
+ @Presubmit
public void allPreferencesShouldHaveUniqueKey()
throws IOException, XmlPullParserException, Resources.NotFoundException {
final Set<String> uniqueKeys = new HashSet<>();