Merge "Move strings to SettingsLib so TV can use them"
diff --git a/res/layout-land/fingerprint_enroll_finish.xml b/res/layout-land/fingerprint_enroll_finish.xml
index b9e2a64..bb7e295 100644
--- a/res/layout-land/fingerprint_enroll_finish.xml
+++ b/res/layout-land/fingerprint_enroll_finish.xml
@@ -64,16 +64,6 @@
android:layout_height="wrap_content"
android:text="@string/security_settings_fingerprint_enroll_finish_message"/>
- <TextView
- android:id="@+id/message_secondary"
- style="@style/SuwDescription.Glif"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/suw_description_glif_margin_top"
- android:text="@string/setup_fingerprint_enroll_finish_message_secondary"
- android:textColor="?android:attr/textColorSecondary"
- android:visibility="gone" />
-
<Space
android:layout_width="0dp"
android:layout_height="0dp"
diff --git a/res/layout/fingerprint_enroll_finish_base.xml b/res/layout/fingerprint_enroll_finish_base.xml
index f0bd882..f666942 100644
--- a/res/layout/fingerprint_enroll_finish_base.xml
+++ b/res/layout/fingerprint_enroll_finish_base.xml
@@ -40,16 +40,6 @@
android:layout_marginTop="@dimen/suw_description_glif_margin_top"
android:text="@string/security_settings_fingerprint_enroll_finish_message"/>
- <TextView
- android:id="@+id/message_secondary"
- style="@style/SuwDescription.Glif"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/suw_description_glif_margin_top"
- android:text="@string/setup_fingerprint_enroll_finish_message_secondary"
- android:textColor="?android:attr/textColorSecondary"
- android:visibility="gone" />
-
<com.android.setupwizardlib.view.FillContentLayout
android:layout_width="match_parent"
android:layout_height="0dp"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 655a7a8..39c31ac 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -819,9 +819,9 @@
<!-- Introduction detail message shown in fingerprint enrollment dialog [CHAR LIMIT=NONE]-->
<string name="security_settings_fingerprint_enroll_introduction_message">Just touch the fingerprint sensor to unlock your phone, authorize purchases, or sign in to apps. Be careful whose fingerprints you add. Even one added print can do any of these things.\n\nNote: Your fingerprint may be less secure than a strong pattern or PIN.</string>
<!-- Introduction detail message shown in fingerprint enrollment dialog, when fingerprint unlock is disabled by device admin [CHAR LIMIT=NONE]-->
- <string name="security_settings_fingerprint_enroll_introduction_message_unlock_disabled">Just touch the fingerprint sensor to authorize purchases, or sign in to apps. Be careful whose fingerprints you add. Even one added print can do any of these things.\n\nNote: You can\u2019t use your fingerprint to unlock this device. For more information, contact your organization\u2019s admin.</string>
+ <string name="security_settings_fingerprint_enroll_introduction_message_unlock_disabled">Use your fingerprint to unlock your phone or approve purchases.\n\nNote: You can\u2019t use your fingerprint to unlock this device. For more information, contact your organization\u2019s admin.</string>
<!-- Introduction detail message shown in fingerprint enrollment screen in setup wizard. [CHAR LIMIT=NONE]-->
- <string name="security_settings_fingerprint_enroll_introduction_message_setup">Just touch the fingerprint sensor to unlock your phone, authorize purchases, or sign in to apps. Be careful whose fingerprints you add. Even one added print can do any of these things.\n\nNote: Your fingerprint may be less secure than a strong pattern or PIN.</string>
+ <string name="security_settings_fingerprint_enroll_introduction_message_setup">Use your fingerprint to unlock your phone or approve purchases.\n\nNote: Your fingerprint may be less secure than a strong pattern or PIN.</string>
<!-- Button text to cancel enrollment from the introduction [CHAR LIMIT=22] -->
<string name="security_settings_fingerprint_enroll_introduction_cancel">Cancel</string>
<!-- Button text to continue to the next screen from the introduction [CHAR LIMIT=22] -->
@@ -829,28 +829,27 @@
<!-- Button text to cancel enrollment from the introduction (this string variant is used while in setup wizard) [CHAR LIMIT=22] -->
<string name="security_settings_fingerprint_enroll_introduction_cancel_setup">Skip</string>
<!-- Button text to continue to the next screen from the introduction (this string variant is used while in setup wizard) [CHAR LIMIT=22] -->
- <string name="security_settings_fingerprint_enroll_introduction_continue_setup">Add fingerprint</string>
+ <string name="security_settings_fingerprint_enroll_introduction_continue_setup">Next</string>
<!-- Dialog text shown when the user tries to skip setting up a screen lock, warning them of potential consequences of not doing so, including loss of factory reset protection. (tablet) [CHAR LIMIT=NONE] -->
- <string name="lock_screen_intro_skip_dialog_text_frp" product="tablet">Device protection features will not be activated. You won\u2019t be able to prevent others from using this tablet if it\u2019s lost, stolen or wiped.</string>
+ <string name="lock_screen_intro_skip_dialog_text_frp" product="tablet">Device protection features won\u2019t be turned on. You won\u2019t be able to prevent others from using this tablet if it\u2019s lost, stolen or reset.</string>
<!-- Dialog text shown when the user tries to skip setting up a screen lock, warning them of potential consequences of not doing so, including loss of factory reset protection. (device) [CHAR LIMIT=NONE] -->
- <string name="lock_screen_intro_skip_dialog_text_frp" product="device">Device protection features will not be activated. You won\u2019t be able to prevent others from using this device if it\u2019s lost, stolen or wiped.</string>
+ <string name="lock_screen_intro_skip_dialog_text_frp" product="device">Device protection features won\u2019t be turned on. You won\u2019t be able to prevent others from using this device if it\u2019s lost, stolen or reset.</string>
<!-- Dialog text shown when the user tries to skip setting up a screen lock, warning them of potential consequences of not doing so, including loss of factory reset protection. (phone) [CHAR LIMIT=NONE] -->
- <string name="lock_screen_intro_skip_dialog_text_frp" product="default">Device protection features will not be activated. You won\u2019t be able to prevent others from using this phone if it\u2019s lost, stolen or wiped.</string>
+ <string name="lock_screen_intro_skip_dialog_text_frp" product="default">Device protection features won\u2019t be turned on. You won\u2019t be able to prevent others from using this phone if it\u2019s lost, stolen or reset.</string>
<!-- Dialog text shown when the user tries to skip setting up a screen lock, warning them of potential consequences of not doing so. (tablet) [CHAR LIMIT=NONE] -->
- <string name="lock_screen_intro_skip_dialog_text" product="tablet">Device protection features will not be activated. You won\u2019t be able to prevent others from using this tablet if it\u2019s lost or stolen.</string>
+ <string name="lock_screen_intro_skip_dialog_text" product="tablet">Device protection features won\u2019t be turned on. You won\u2019t be able to prevent others from using this tablet if it\u2019s lost or stolen.</string>
<!-- Dialog text shown when the user tries to skip setting up a screen lock, warning them of potential consequences of not doing so. (device) [CHAR LIMIT=NONE] -->
- <string name="lock_screen_intro_skip_dialog_text" product="device">Device protection features will not be activated. You won\u2019t be able to prevent others from using this device if it\u2019s lost or stolen.</string>
+ <string name="lock_screen_intro_skip_dialog_text" product="device">Device protection features won\u2019t be turned on. You won\u2019t be able to prevent others from using this device if it\u2019s lost or stolen.</string>
<!-- Dialog text shown when the user tries to skip setting up a screen lock, warning them of potential consequences of not doing so. (phone) [CHAR LIMIT=NONE] -->
- <string name="lock_screen_intro_skip_dialog_text" product="default">Device protection features will not be activated. You won\u2019t be able to prevent others from using this phone if it\u2019s lost or stolen.</string>
+ <string name="lock_screen_intro_skip_dialog_text" product="default">Device protection features won\u2019t be turned on. You won\u2019t be able to prevent others from using this phone if it\u2019s lost or stolen.</string>
<!-- Button for skipping a step after having been warned of a potential concern [CHAR LIMIT=30] -->
<string name="skip_anyway_button_label">Skip anyway</string>
<!-- Button for going to the previous screen or step [CHAR LIMIT=20] -->
<string name="go_back_button_label">Go back</string>
<!-- Introduction title shown in fingerprint enrollment dialog to locate the sensor [CHAR LIMIT=29] -->
- <string name="security_settings_fingerprint_enroll_find_sensor_title">Find the sensor</string>
+ <string name="security_settings_fingerprint_enroll_find_sensor_title">Touch the sensor</string>
<!-- Message shown in fingerprint enrollment dialog to locate the sensor -->
- <string name="security_settings_fingerprint_enroll_find_sensor_message">
- Locate the fingerprint sensor on the back of your phone.</string>
+ <string name="security_settings_fingerprint_enroll_find_sensor_message">It\u2019s on the back of your phone. Use your index finger.</string>
<!-- Content description of graphic that shows where the fingerprint of the device is [CHAR LIMIT=NONE] -->
<string name="security_settings_fingerprint_enroll_find_sensor_content_description">Illustration with device and fingerprint sensor location</string>
<!-- Label text shown in fingerprint dialog for renaming a fingerprint template [CHAR LIMIT=22] -->
@@ -860,21 +859,17 @@
<!-- Button text shown in fingerprint dialog that allows the user to delete the fingerprint template [CHAR LIMIT=22] -->
<string name="security_settings_fingerprint_enroll_dialog_delete">Delete</string>
<!-- Title shown in fingerprint enrollment dialog to begin enrollment [CHAR LIMIT=29]-->
- <string name="security_settings_fingerprint_enroll_start_title">Let\u2019s start</string>
+ <string name="security_settings_fingerprint_enroll_start_title">Touch the sensor</string>
<!-- Message shown in fingerprint enrollment dialog to begin enrollment [CHAR LIMIT=NONE] -->
<string name="security_settings_fingerprint_enroll_start_message">Put your finger on the sensor and lift after you feel a vibration</string>
<!-- Title shown in fingerprint enrollment dialog to repeat touching the fingerprint sensor [CHAR LIMIT=29] -->
- <string name="security_settings_fingerprint_enroll_repeat_title">Keep going</string>
+ <string name="security_settings_fingerprint_enroll_repeat_title">Lift, then touch again</string>
<!-- Message shown in fingerprint enrollment dialog to repeat touching the fingerprint sensor [CHAR LIMIT=NONE] -->
- <string name="security_settings_fingerprint_enroll_repeat_message">Move your finger slightly to add all the different parts of your fingerprint</string>
+ <string name="security_settings_fingerprint_enroll_repeat_message">Keep lifting your finger to add the different parts of your fingerprint</string>
<!-- Title shown in fingerprint enrollment dialog once enrollment is completed [CHAR LIMIT=29] -->
- <string name="security_settings_fingerprint_enroll_finish_title">Fingerprint added!</string>
+ <string name="security_settings_fingerprint_enroll_finish_title">Fingerprint added</string>
<!-- Message shown in fingerprint enrollment dialog once enrollment is completed -->
- <string name="security_settings_fingerprint_enroll_finish_message">Whenever you see this icon, you can use your fingerprint for identification or to authorize a purchase.</string>
- <!-- Message shown when fingerprint enrollment is completed during setup wizard [CHAR LIMIT=NONE] -->
- <string name="setup_fingerprint_enroll_finish_message">Just touch the fingerprint sensor to wake and unlock your device.</string>
- <!-- Message shown when fingerprint enrollment is completed, telling user about the fingerprint icon that will be shown whenever they can use their fingerprint [CHAR LIMIT=NONE] -->
- <string name="setup_fingerprint_enroll_finish_message_secondary">When you see this icon, you can also authorize purchases or sign in to apps.</string>
+ <string name="security_settings_fingerprint_enroll_finish_message">When you see this icon, use your fingerprint for identification or to approve purchases.</string>
<!-- Title of the dialog shown when the user tries to skip fingerprint setup, asking them to confirm the action [CHAR LIMIT=40] -->
<string name="setup_fingerprint_enroll_enrolling_skip_title">Skip fingerprint setup?</string>
<!-- Content of the dialog shown when the user tries to skip fingerprint setup, asking them to confirm the action [CHAR LIMIT=NONE] -->
@@ -887,7 +882,7 @@
<!-- Dialog title for dialog which shows when user touches the icon on the screen, instead of the sensor at the back [CHAR LIMIT=45] -->
<string name="security_settings_fingerprint_enroll_touch_dialog_title">Whoops, that\u2019s not the sensor</string>
<!-- Dialog message for dialog which shows when user touches the icon on the screen, instead of the sensor at the back [CHAR LIMIT=45] -->
- <string name="security_settings_fingerprint_enroll_touch_dialog_message">Use the fingerprint sensor on your device.</string>
+ <string name="security_settings_fingerprint_enroll_touch_dialog_message">Touch the sensor on the back of your phone. Use your index finger.</string>
<!-- Dialog message for dialog which shows when finger cannot be enrolled. [CHAR LIMIT=45] -->
<string name="security_settings_fingerprint_enroll_error_dialog_title">Enrollment was not completed</string>
<!-- Dialog message for dialog which shows when finger cannot be enrolled due to being idle too long. -->
@@ -1527,6 +1522,18 @@
<string name="device_details_title">Device details</string>
<!-- Title of the item to show device MAC address -->
<string name="bluetooth_device_mac_address">Device\'s Bluetooth address: <xliff:g id="address">%1$s</xliff:g></string>
+ <!-- Bluetooth device details. The title of a confirmation dialog for unpairing a paired device. [CHAR LIMIT=60] -->
+ <string name="bluetooth_unpair_dialog_title">Forget device?</string>
+
+ <!-- Bluetooth device details. The body of a confirmation dialog for unpairing a paired device. -->
+ <string name="bluetooth_unpair_dialog_body" product="default">Your phone will no longer be paired with <xliff:g id="device_name">%1$s</xliff:g></string>
+ <!-- Bluetooth device details. The body of a confirmation dialog for unpairing a paired device. -->
+ <string name="bluetooth_unpair_dialog_body" product="tablet">Your tablet will no longer be paired with <xliff:g id="device_name">%1$s</xliff:g></string>
+ <!-- Bluetooth device details. The body of a confirmation dialog for unpairing a paired device. -->
+ <string name="bluetooth_unpair_dialog_body" product="device">Your device will no longer be paired with <xliff:g id="device_name">%1$s</xliff:g></string>
+
+ <!-- Bluetooth device details. In the confirmation dialog for unpairing a paired device, this is the label on the button that will complete the unpairing action. -->
+ <string name="bluetooth_unpair_dialog_forget_confirm_button">Forget device</string>
<!-- Bluetooth settings. The title of the screen to pick which profiles to connect to on the device. For example, headphones may have both A2DP and headset, this allows the user to choose which one he wants to connect to. -->
<string name="bluetooth_connect_specific_profiles_title">Connect to\u2026</string>
diff --git a/src/com/android/settings/fingerprint/SetupFingerprintEnrollFinish.java b/src/com/android/settings/fingerprint/SetupFingerprintEnrollFinish.java
index 4d7395d..43ff097 100644
--- a/src/com/android/settings/fingerprint/SetupFingerprintEnrollFinish.java
+++ b/src/com/android/settings/fingerprint/SetupFingerprintEnrollFinish.java
@@ -49,12 +49,6 @@
@Override
protected void initViews() {
super.initViews();
-
- final TextView message = (TextView) findViewById(R.id.message);
- message.setText(R.string.setup_fingerprint_enroll_finish_message);
-
- final TextView secondaryMessage = (TextView) findViewById(R.id.message_secondary);
- secondaryMessage.setVisibility(View.VISIBLE);
}
@Override
diff --git a/src/com/android/settings/search/InlineSwitchViewHolder.java b/src/com/android/settings/search/InlineSwitchViewHolder.java
index 162eb1a..bb8320d 100644
--- a/src/com/android/settings/search/InlineSwitchViewHolder.java
+++ b/src/com/android/settings/search/InlineSwitchViewHolder.java
@@ -46,7 +46,7 @@
}
@Override
- public void onBind(SearchFragment fragment, SearchResult result) {
+ public void onBind(SearchFragment fragment, final SearchResult result) {
super.onBind(fragment, result);
if (mContext == null) {
return;
@@ -57,7 +57,7 @@
final Pair<Integer, Object> value = Pair.create(
MetricsEvent.FIELD_SETTINGS_SEARCH_INLINE_RESULT_VALUE, isChecked
? 1L : 0L);
- fragment.onSearchResultClicked(this, payload.mSettingKey, value);
+ fragment.onSearchResultClicked(this, result, value);
int newValue = isChecked ? InlineSwitchPayload.TRUE : InlineSwitchPayload.FALSE;
payload.setValue(mContext, newValue);
});
diff --git a/src/com/android/settings/search/IntentSearchViewHolder.java b/src/com/android/settings/search/IntentSearchViewHolder.java
index 17ff981..11adaef 100644
--- a/src/com/android/settings/search/IntentSearchViewHolder.java
+++ b/src/com/android/settings/search/IntentSearchViewHolder.java
@@ -16,13 +16,9 @@
*/
package com.android.settings.search;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.text.TextUtils;
import android.view.View;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settings.SettingsActivity;
/**
* ViewHolder for intent based search results.
@@ -44,14 +40,8 @@
super.onBind(fragment, result);
itemView.setOnClickListener(v -> {
- final Intent intent = result.payload.getIntent();
- final ComponentName cn = intent.getComponent();
- String resultName = intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT);
- if (TextUtils.isEmpty(resultName) && cn != null) {
- resultName = cn.flattenToString();
- }
- fragment.onSearchResultClicked(this, resultName);
- fragment.startActivity(intent);
+ fragment.onSearchResultClicked(this, result);
+ fragment.startActivity(result.payload.getIntent());
});
}
}
diff --git a/src/com/android/settings/search/SearchFeatureProvider.java b/src/com/android/settings/search/SearchFeatureProvider.java
index 67d4ef1..50edae7 100644
--- a/src/com/android/settings/search/SearchFeatureProvider.java
+++ b/src/com/android/settings/search/SearchFeatureProvider.java
@@ -22,6 +22,7 @@
import android.view.View;
import com.android.settings.dashboard.SiteMapManager;
+import com.android.settings.search.ranking.SearchResultsRankerCallback;
import java.util.List;
@@ -98,21 +99,30 @@
}
/**
- * Ranks search results based on the input query.
+ * Query search results based on the input query.
*
+ * @param context application context
* @param query input user query
- * @param searchResults list of search results to be ranked
+ * @param searchResultsRankerCallback {@link SearchResultsRankerCallback}
*/
- default void rankSearchResults(String query, List<SearchResult> searchResults) {
+ 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
* @param query input user query
* @param searchResult clicked result
*/
- default void searchResultClicked(String query, SearchResult searchResult) {
+ default void searchResultClicked(Context context, String query, SearchResult searchResult) {
}
/**
diff --git a/src/com/android/settings/search/SearchFragment.java b/src/com/android/settings/search/SearchFragment.java
index 6b07f2d..87df62e 100644
--- a/src/com/android/settings/search/SearchFragment.java
+++ b/src/com/android/settings/search/SearchFragment.java
@@ -19,7 +19,9 @@
import android.app.Activity;
import android.app.LoaderManager;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.Loader;
import android.os.Bundle;
import android.support.annotation.VisibleForTesting;
@@ -37,6 +39,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
+import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
@@ -234,6 +237,7 @@
mSavedQueryController.loadSavedQueries();
mSearchFeatureProvider.hideFeedbackButton();
} else {
+ mSearchAdapter.initializeSearch(mQuery);
restartLoaders();
}
@@ -270,15 +274,7 @@
return;
}
- final int resultCount = mSearchAdapter.displaySearchResults(mQuery);
-
- if (resultCount == 0) {
- mNoResultsView.setVisibility(View.VISIBLE);
- } else {
- mNoResultsView.setVisibility(View.GONE);
- mResultsRecyclerView.scrollToPosition(0);
- }
- mSearchFeatureProvider.showFeedbackButton(this, getView());
+ mSearchAdapter.notifyResultsLoaded();
}
@Override
@@ -304,30 +300,24 @@
requery();
}
- public void onSearchResultClicked(SearchViewHolder result, String settingName,
+ public void onSearchResultClicked(SearchViewHolder resultViewHolder, SearchResult result,
Pair<Integer, Object>... logTaggedData) {
- final List<Pair<Integer, Object>> taggedData = new ArrayList<>();
- if (logTaggedData != null) {
- taggedData.addAll(Arrays.asList(logTaggedData));
- }
- taggedData.add(Pair.create(
- MetricsEvent.FIELD_SETTINGS_SERACH_RESULT_COUNT,
- mSearchAdapter.getItemCount()));
- taggedData.add(Pair.create(
- MetricsEvent.FIELD_SETTINGS_SERACH_RESULT_RANK,
- result.getAdapterPosition()));
- taggedData.add(Pair.create(
- MetricsEvent.FIELD_SETTINGS_SERACH_QUERY_LENGTH,
- TextUtils.isEmpty(mQuery) ? 0 : mQuery.length()));
+ logSearchResultClicked(resultViewHolder, result, logTaggedData);
- mMetricsFeatureProvider.action(getContext(),
- MetricsEvent.ACTION_CLICK_SETTINGS_SEARCH_RESULT,
- settingName,
- taggedData.toArray(new Pair[0]));
mSavedQueryController.saveQuery(mQuery);
mResultClickCount++;
}
+ public void onSearchResultsDisplayed(int resultCount) {
+ if (resultCount == 0) {
+ mNoResultsView.setVisibility(View.VISIBLE);
+ } else {
+ mNoResultsView.setVisibility(View.GONE);
+ mResultsRecyclerView.scrollToPosition(0);
+ }
+ mSearchFeatureProvider.showFeedbackButton(this, getView());
+ }
+
public void onSavedQueryClicked(CharSequence query) {
final String queryString = query.toString();
mMetricsFeatureProvider.action(getContext(),
@@ -378,4 +368,38 @@
mResultsRecyclerView.requestFocus();
}
}
-}
\ No newline at end of file
+
+ private void logSearchResultClicked(SearchViewHolder resultViewHolder, SearchResult result,
+ Pair<Integer, Object>... logTaggedData) {
+ final Intent intent = result.payload.getIntent();
+ if (intent == null) {
+ Log.w(TAG, "Skipped logging click on search result because of null intent, which can " +
+ "happen on saved query results.");
+ return;
+ }
+ final ComponentName cn = intent.getComponent();
+ String resultName = intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT);
+ if (TextUtils.isEmpty(resultName) && cn != null) {
+ resultName = cn.flattenToString();
+ }
+ final List<Pair<Integer, Object>> taggedData = new ArrayList<>();
+ if (logTaggedData != null) {
+ taggedData.addAll(Arrays.asList(logTaggedData));
+ }
+ taggedData.add(Pair.create(
+ MetricsEvent.FIELD_SETTINGS_SERACH_RESULT_COUNT,
+ mSearchAdapter.getItemCount()));
+ taggedData.add(Pair.create(
+ MetricsEvent.FIELD_SETTINGS_SERACH_RESULT_RANK,
+ resultViewHolder.getAdapterPosition()));
+ taggedData.add(Pair.create(
+ MetricsEvent.FIELD_SETTINGS_SERACH_QUERY_LENGTH,
+ TextUtils.isEmpty(mQuery) ? 0 : mQuery.length()));
+
+ mMetricsFeatureProvider.action(getContext(),
+ MetricsEvent.ACTION_CLICK_SETTINGS_SEARCH_RESULT,
+ resultName,
+ taggedData.toArray(new Pair[0]));
+ mSearchFeatureProvider.searchResultClicked(getContext(), mQuery, result);
+ }
+}
diff --git a/src/com/android/settings/search/SearchResultsAdapter.java b/src/com/android/settings/search/SearchResultsAdapter.java
index 31e0793..199930f 100644
--- a/src/com/android/settings/search/SearchResultsAdapter.java
+++ b/src/com/android/settings/search/SearchResultsAdapter.java
@@ -18,36 +18,79 @@
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;
-public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder> {
+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 int MSG_RANKING_TIMED_OUT = 1;
+
+ // TODO(b/38197948): Tune this timeout based on latency of static and async rankings. Also, we
+ // should add a gservices flag to control this.
+ private static final long RANKING_TIMEOUT_MS = 300;
private final SearchFragment mFragment;
-
- private List<SearchResult> mSearchResults;
+ 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 {}
+ private static final int DISABLED = 0;
+ private static final int PENDING_RESULTS = 1;
+ private static final int SUCCEEDED = 2;
+ private static final int FAILED = 3;
+ private static final int TIMED_OUT = 4;
+ private @AsyncRankingState int mAsyncRankingState;
public SearchResultsAdapter(SearchFragment fragment,
SearchFeatureProvider searchFeatureProvider) {
mFragment = fragment;
+ mContext = fragment.getContext().getApplicationContext();
mSearchResults = new ArrayList<>();
mResultsMap = new ArrayMap<>();
+ mSearchRankingScores = new ArrayList<>();
+ mStaticallyRankedSearchResults = new ArrayList<>();
mSearchFeatureProvider = searchFeatureProvider;
setHasStableIds(true);
@@ -93,7 +136,37 @@
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.
@@ -120,71 +193,24 @@
}
/**
- * Merge the results from each of the loaders into one list for the adapter.
- * Prioritizes results from the local database over installed apps.
- *
- * @param query user query corresponding to these results
- * @return Number of matched results
+ * Notifies the adapter that all the unsorted results are loaded and now the ladapter can
+ * proceed with ranking the results.
*/
- public int displaySearchResults(String query) {
- List<? extends SearchResult> databaseResults = null;
- List<? extends SearchResult> installedAppResults = null;
- final String dbLoaderKey = DatabaseResultLoader.class.getName();
- final String appLoaderKey = InstalledAppResultLoader.class.getName();
- int dbSize = 0;
- int appSize = 0;
- if (mResultsMap.containsKey(dbLoaderKey)) {
- databaseResults = new ArrayList<>(mResultsMap.get(dbLoaderKey));
- dbSize = databaseResults.size();
- Collections.sort(databaseResults);
+ @MainThread
+ public void notifyResultsLoaded() {
+ mSearchResultsLoaded = true;
+ // static ranking is skipped only if asyc ranking is already succeeded.
+ if (mAsyncRankingState != SUCCEEDED) {
+ doStaticRanking();
}
- if (mResultsMap.containsKey(appLoaderKey)) {
- installedAppResults = new ArrayList<>(mResultsMap.get(appLoaderKey));
- appSize = installedAppResults.size();
- Collections.sort(installedAppResults);
+ if (canUpdateSearchResults()) {
+ updateSearchResults();
}
- final List<SearchResult> newResults = new ArrayList<>(dbSize + appSize);
-
- int dbIndex = 0;
- int appIndex = 0;
- int rank = SearchResult.TOP_RANK;
-
- while (rank <= SearchResult.BOTTOM_RANK) {
- while ((dbIndex < dbSize) && (databaseResults.get(dbIndex).rank == rank)) {
- newResults.add(databaseResults.get(dbIndex++));
- }
- while ((appIndex < appSize) && (installedAppResults.get(appIndex).rank == rank)) {
- newResults.add(installedAppResults.get(appIndex++));
- }
- rank++;
- }
-
- while (dbIndex < dbSize) {
- newResults.add(databaseResults.get(dbIndex++));
- }
- while (appIndex < appSize) {
- newResults.add(installedAppResults.get(appIndex++));
- }
-
- final boolean isSmartSearchRankingEnabled = mSearchFeatureProvider
- .isSmartSearchRankingEnabled(mFragment.getContext().getApplicationContext());
-
- if (isSmartSearchRankingEnabled) {
- // TODO: run this in parallel to loading the results if takes too long
- mSearchFeatureProvider.rankSearchResults(query, newResults);
- }
-
- final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(
- new SearchResultDiffCallback(mSearchResults, newResults),
- isSmartSearchRankingEnabled);
- mSearchResults = newResults;
- diffResult.dispatchUpdatesTo(this);
-
- return mSearchResults.size();
}
public void clearResults() {
mSearchResults.clear();
+ mStaticallyRankedSearchResults.clear();
mResultsMap.clear();
notifyDataSetChanged();
}
@@ -193,4 +219,178 @@
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();
+ handler.sendMessageDelayed(
+ handler.obtainMessage(MSG_RANKING_TIMED_OUT), RANKING_TIMEOUT_MS);
+ mSearchFeatureProvider.querySearchResults(mContext, query, this);
+ } else {
+ mAsyncRankingState = DISABLED;
+ }
+ }
+
+ /**
+ * 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);
+ int dbSize = databaseResults.size();
+ int appSize = installedAppResults.size();
+
+ int dbIndex = 0;
+ int appIndex = 0;
+ int rank = SearchResult.TOP_RANK;
+
+ 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++));
+ }
+ rank++;
+ }
+
+ while (dbIndex < dbSize) {
+ mStaticallyRankedSearchResults.add(databaseResults.get(dbIndex++));
+ }
+ while (appIndex < appSize) {
+ mStaticallyRankedSearchResults.add(installedAppResults.get(appIndex++));
+ }
+ }
+
+ 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);
+ int dbSize = databaseResults.size();
+ int appSize = installedAppResults.size();
+
+ final List<SearchResult> asyncRankingResults = new ArrayList<>(dbSize + appSize);
+ List<SearchResult> databaseResultsSortedByScores = new ArrayList<>(databaseResults);
+ Collections.sort(databaseResultsSortedByScores, 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;
+ }
+ }
+ });
+ asyncRankingResults.addAll(databaseResultsSortedByScores);
+ // App results are not ranked by async ranking and appended at the end of the list.
+ asyncRankingResults.addAll(installedAppResults);
+ return asyncRankingResults;
+ }
+
+ @VisibleForTesting
+ Set<? extends SearchResult> getUnsortedLoadedResults(String loaderKey) {
+ return mResultsMap.containsKey(loaderKey) ?
+ mResultsMap.get(loaderKey) : new HashSet<SearchResult>();
+ }
+
+ @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;
+ }
+
+ private void postSearchResults(List<SearchResult> newSearchResults, boolean detectMoves) {
+ final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(
+ new SearchResultDiffCallback(mSearchResults, newSearchResults), detectMoves);
+ mSearchResults.clear();
+ mSearchResults.addAll(newSearchResults);
+ diffResult.dispatchUpdatesTo(this);
+ mFragment.onSearchResultsDisplayed(mSearchResults.size());
+ mSearchResultsUpdated = true;
+ }
}
diff --git a/src/com/android/settings/search/ranking/SearchResultsRankerCallback.java b/src/com/android/settings/search/ranking/SearchResultsRankerCallback.java
new file mode 100644
index 0000000..c254a40
--- /dev/null
+++ b/src/com/android/settings/search/ranking/SearchResultsRankerCallback.java
@@ -0,0 +1,38 @@
+/*
+ * 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.ranking;
+
+import android.util.Pair;
+
+import java.util.List;
+
+public interface SearchResultsRankerCallback {
+
+ /**
+ * Called when ranker provides the ranking scores.
+ * @param searchRankingScores Ordered List of Pairs of String and Float corresponding to
+ * stableIds and ranking scores. The list must be descendingly
+ * ordered based on scores.
+ */
+ public void onRankingScoresAvailable(List<Pair<String, Float>> searchRankingScores);
+
+ /**
+ * Called when for any reason ranker fails, which notifies the client to proceed
+ * without ranking results.
+ */
+ public void onRankingFailed();
+}
diff --git a/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java b/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java
index 1a60256..bf67565 100644
--- a/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java
+++ b/src/com/android/settings/wifi/SavedAccessPointsWifiSettings.java
@@ -38,7 +38,7 @@
import com.android.settings.search.SearchIndexableRaw;
import com.android.settingslib.wifi.AccessPoint;
import com.android.settingslib.wifi.AccessPointPreference;
-import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiSavedConfigUtils;
import java.util.ArrayList;
import java.util.Arrays;
@@ -124,7 +124,8 @@
PreferenceScreen preferenceScreen = getPreferenceScreen();
final Context context = getPrefContext();
- final List<AccessPoint> accessPoints = getSavedConfigs(context, mWifiManager);
+ final List<AccessPoint> accessPoints =
+ WifiSavedConfigUtils.getAllConfigs(context, mWifiManager);
Collections.sort(accessPoints, SAVED_NETWORK_COMPARATOR);
preferenceScreen.removeAll();
@@ -142,39 +143,6 @@
}
}
- /**
- * Retrieved the list of saved network configurations from {@link WifiManager}.
- * Each configuration is represented by {@link AccessPoint}.
- *
- * @param context The application context
- * @param wifiManager An instance of {@link WifiManager}
- * @return List of {@link AccessPoint}
- */
- private static List<AccessPoint> getSavedConfigs(Context context, WifiManager wifiManager) {
- List<AccessPoint> savedConfigs = new ArrayList<>();
- List<WifiConfiguration> savedNetworks = wifiManager.getConfiguredNetworks();
- for (WifiConfiguration network : savedNetworks) {
- // Configuration for Passpoint network is configured temporary by WifiService for
- // connection attempt only. The underlying configuration is saved as Passpoint
- // configuration, which will be retrieved with WifiManager#getPasspointConfiguration
- // call below.
- if (network.isPasspoint()) {
- continue;
- }
- savedConfigs.add(new AccessPoint(context, network));
- }
- try {
- List<PasspointConfiguration> savedPasspointConfigs =
- wifiManager.getPasspointConfigurations();
- for (PasspointConfiguration config : savedPasspointConfigs) {
- savedConfigs.add(new AccessPoint(context, config));
- }
- } catch (UnsupportedOperationException e) {
- // Passpoint not supported.
- }
- return savedConfigs;
- }
-
private void showDialog(LongPressAccessPointPreference accessPoint, boolean edit) {
if (mDialog != null) {
removeDialog(WifiSettings.WIFI_DIALOG_ID);
@@ -292,8 +260,8 @@
result.add(data);
// Add available Wi-Fi access points
- final List<AccessPoint> accessPoints =
- getSavedConfigs(context, context.getSystemService(WifiManager.class));
+ final List<AccessPoint> accessPoints = WifiSavedConfigUtils.getAllConfigs(
+ context, context.getSystemService(WifiManager.class));
final int accessPointsSize = accessPoints.size();
for (int i = 0; i < accessPointsSize; ++i){
diff --git a/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java b/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java
index ba1408f7..efeeffc 100644
--- a/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java
+++ b/tests/robotests/src/com/android/settings/search/IntentSearchViewHolderTest.java
@@ -97,7 +97,7 @@
assertThat(mHolder.summaryView.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mHolder.breadcrumbView.getVisibility()).isEqualTo(View.GONE);
- verify(mFragment).onSearchResultClicked(eq(mHolder), anyString());
+ verify(mFragment).onSearchResultClicked(eq(mHolder), any(SearchResult.class));
verify(mFragment).startActivity(any(Intent.class));
}
diff --git a/tests/robotests/src/com/android/settings/search/SearchFragmentTest.java b/tests/robotests/src/com/android/settings/search/SearchFragmentTest.java
index 44851b1..6adc895 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 android.app.LoaderManager;
+import android.content.Intent;
import android.content.Context;
import android.content.Loader;
import android.os.Bundle;
@@ -26,6 +27,7 @@
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
+import com.android.settings.SettingsActivity;
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.testutils.DatabaseTestUtils;
@@ -36,7 +38,9 @@
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;
@@ -75,6 +79,11 @@
private SavedQueryLoader mSavedQueryLoader;
@Mock
private SavedQueryController mSavedQueryController;
+ @Mock
+ private SearchResultsAdapter mSearchResultsAdapter;
+ @Captor
+ private ArgumentCaptor<String> mQueryCaptor = ArgumentCaptor.forClass(String.class);
+
private FakeFeatureFactory mFeatureFactory;
@Before
@@ -148,7 +157,7 @@
}
@Test
- public void queryTextChange_shouldTriggerLoader() {
+ public void queryTextChange_shouldTriggerLoaderAndInitializeSearch() {
when(mFeatureFactory.searchFeatureProvider
.getDatabaseSearchLoader(any(Context.class), anyString()))
.thenReturn(mDatabaseResultLoader);
@@ -167,6 +176,7 @@
when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class)))
.thenReturn(true);
+ ReflectionHelpers.setField(fragment, "mSearchAdapter", mSearchResultsAdapter);
fragment.onQueryTextChange(testQuery);
activityController.get().onBackPressed();
@@ -181,10 +191,12 @@
.getDatabaseSearchLoader(any(Context.class), anyString());
verify(mFeatureFactory.searchFeatureProvider)
.getInstalledAppSearchLoader(any(Context.class), anyString());
+ verify(mSearchResultsAdapter).initializeSearch(mQueryCaptor.capture());
+ assertThat(mQueryCaptor.getValue()).isEqualTo(testQuery);
}
@Test
- public void queryTextChangeToEmpty_shouldLoadSavedQuery() {
+ public void queryTextChangeToEmpty_shouldLoadSavedQueryAndNotInitializeSearch() {
when(mFeatureFactory.searchFeatureProvider
.getDatabaseSearchLoader(any(Context.class), anyString()))
.thenReturn(mDatabaseResultLoader);
@@ -201,6 +213,7 @@
when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class)))
.thenReturn(true);
ReflectionHelpers.setField(fragment, "mSavedQueryController", mSavedQueryController);
+ ReflectionHelpers.setField(fragment, "mSearchAdapter", mSearchResultsAdapter);
fragment.mQuery = "123";
fragment.onQueryTextChange("");
@@ -210,6 +223,7 @@
verify(mFeatureFactory.searchFeatureProvider, never())
.getInstalledAppSearchLoader(any(Context.class), anyString());
verify(mSavedQueryController).loadSavedQueries();
+ verify(mSearchResultsAdapter, never()).initializeSearch(anyString());
}
@Test
@@ -383,12 +397,21 @@
SearchFragment fragment = new SearchFragment();
ReflectionHelpers.setField(fragment, "mMetricsFeatureProvider",
mFeatureFactory.metricsFeatureProvider);
+ ReflectionHelpers.setField(fragment, "mSearchFeatureProvider",
+ mFeatureFactory.searchFeatureProvider);
ReflectionHelpers.setField(fragment, "mSearchAdapter", mock(SearchResultsAdapter.class));
fragment.mSavedQueryController = mock(SavedQueryController.class);
// Should log result name, result count, clicked rank, etc.
- final SearchViewHolder result = mock(SearchViewHolder.class);
- fragment.onSearchResultClicked(result, "test_setting");
+ final SearchViewHolder resultViewHolder = mock(SearchViewHolder.class);
+ ResultPayload payLoad = new ResultPayload(
+ (new Intent()).putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, "test_setting"));
+ SearchResult searchResult = new SearchResult.Builder()
+ .setStableId(payLoad.hashCode())
+ .setPayload(payLoad)
+ .setTitle("setting_title")
+ .build();
+ fragment.onSearchResultClicked(resultViewHolder, searchResult);
verify(mFeatureFactory.metricsFeatureProvider).action(
nullable(Context.class),
@@ -397,6 +420,9 @@
argThat(pairMatches(MetricsProto.MetricsEvent.FIELD_SETTINGS_SERACH_RESULT_COUNT)),
argThat(pairMatches(MetricsProto.MetricsEvent.FIELD_SETTINGS_SERACH_RESULT_RANK)),
argThat(pairMatches(MetricsProto.MetricsEvent.FIELD_SETTINGS_SERACH_QUERY_LENGTH)));
+
+ verify(mFeatureFactory.searchFeatureProvider).searchResultClicked(nullable(Context.class),
+ nullable(String.class), eq(searchResult));
}
private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag) {
diff --git a/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java b/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java
index 8290348..c6a5451 100644
--- a/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java
@@ -21,6 +21,7 @@
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;
@@ -28,24 +29,31 @@
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.search.SearchResult.Builder;
+import com.android.settings.search.ranking.SearchResultsRankerCallback;
+import static org.junit.Assert.assertNull;
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 static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyList;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -63,6 +71,9 @@
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;
@@ -73,10 +84,10 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = Robolectric.buildActivity(Activity.class).get();
- mAdapter = new SearchResultsAdapter(mFragment, mSearchFeatureProvider);
mLoaderClassName = DatabaseResultLoader.class.getName();
when(mFragment.getContext()).thenReturn(mMockContext);
when(mMockContext.getApplicationContext()).thenReturn(mContext);
+ mAdapter = new SearchResultsAdapter(mFragment, mSearchFeatureProvider);
}
@Test
@@ -88,8 +99,9 @@
@Test
public void testSingleSourceMerge_exactCopyReturned() {
Set<SearchResult> intentResults = getIntentSampleResults();
+ mAdapter.initializeSearch("");
mAdapter.addSearchResults(intentResults, mLoaderClassName);
- mAdapter.displaySearchResults("");
+ mAdapter.notifyResultsLoaded();
List<SearchResult> updatedResults = mAdapter.getSearchResults();
assertThat(updatedResults).containsAllIn(intentResults);
@@ -113,11 +125,12 @@
@Test
public void testEndToEndSearch_properResultsMerged_correctOrder() {
+ mAdapter.initializeSearch("");
mAdapter.addSearchResults(new HashSet<SearchResult>(getDummyAppResults()),
InstalledAppResultLoader.class.getName());
mAdapter.addSearchResults(new HashSet<SearchResult>(getDummyDbResults()),
DatabaseResultLoader.class.getName());
- int count = mAdapter.displaySearchResults("");
+ mAdapter.notifyResultsLoaded();
List<SearchResult> results = mAdapter.getSearchResults();
assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
@@ -126,25 +139,28 @@
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
- assertThat(count).isEqualTo(6);
+ verify(mFragment).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
+ assertThat(mSearchResultsCountCaptor.getValue()).isEqualTo(6);
}
@Test
public void testEndToEndSearch_addResults_resultsAddedInOrder() {
- List<AppSearchResult> appResults = getDummyAppResults();
+ List<SearchResult> appResults = getDummyAppResults();
List<SearchResult> dbResults = getDummyDbResults();
+ mAdapter.initializeSearch("");
// Add two individual items
mAdapter.addSearchResults(new HashSet<SearchResult>(appResults.subList(0, 1)),
InstalledAppResultLoader.class.getName());
mAdapter.addSearchResults(new HashSet<SearchResult>(dbResults.subList(0, 1)),
DatabaseResultLoader.class.getName());
- mAdapter.displaySearchResults("");
+ mAdapter.notifyResultsLoaded();
// Add super-set of items
+ mAdapter.initializeSearch("");
mAdapter.addSearchResults(
new HashSet<SearchResult>(appResults), InstalledAppResultLoader.class.getName());
mAdapter.addSearchResults(
new HashSet<SearchResult>(dbResults), DatabaseResultLoader.class.getName());
- int count = mAdapter.displaySearchResults("");
+ mAdapter.notifyResultsLoaded();
List<SearchResult> results = mAdapter.getSearchResults();
assertThat(results.get(0).title).isEqualTo(TITLES[0]); // alpha
@@ -153,46 +169,333 @@
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
- assertThat(count).isEqualTo(6);
- }
-
- @Test
- public void testDisplayResults_ShouldNotRunSmartRankingIfDisabled() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any()))
- .thenReturn(false);
- mAdapter.displaySearchResults("");
- verify(mSearchFeatureProvider, never()).rankSearchResults(anyString(), anyList());
- }
-
- @Test
- public void testDisplayResults_ShouldRunSmartRankingIfEnabled() {
- when(mSearchFeatureProvider.isSmartSearchRankingEnabled(any()))
- .thenReturn(true);
- mAdapter.displaySearchResults("");
- verify(mSearchFeatureProvider, times(1)).rankSearchResults(anyString(), anyList());
+ verify(mFragment, times(2)).onSearchResultsDisplayed(mSearchResultsCountCaptor.capture());
+ assertThat(mSearchResultsCountCaptor.getAllValues().toArray())
+ .isEqualTo(new Integer[] {2, 6});
}
@Test
public void testEndToEndSearch_removeResults_resultsAdded() {
- List<AppSearchResult> appResults = getDummyAppResults();
+ List<SearchResult> appResults = getDummyAppResults();
List<SearchResult> dbResults = getDummyDbResults();
// Add list of items
+ mAdapter.initializeSearch("");
mAdapter.addSearchResults(new HashSet<SearchResult>(appResults),
InstalledAppResultLoader.class.getName());
mAdapter.addSearchResults(new HashSet<SearchResult>(dbResults),
DatabaseResultLoader.class.getName());
- mAdapter.displaySearchResults("");
+ mAdapter.notifyResultsLoaded();
// Add subset of items
+ mAdapter.initializeSearch("");
mAdapter.addSearchResults(new HashSet<SearchResult>(appResults.subList(0, 1)),
InstalledAppResultLoader.class.getName());
mAdapter.addSearchResults(new HashSet<>(dbResults.subList(0, 1)),
DatabaseResultLoader.class.getName());
- int count = mAdapter.displaySearchResults("");
+ mAdapter.notifyResultsLoaded();
List<SearchResult> results = mAdapter.getSearchResults();
assertThat(results.get(0).title).isEqualTo(TITLES[0]);
assertThat(results.get(1).title).isEqualTo(TITLES[3]);
- assertThat(count).isEqualTo(2);
+ 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);
+ }
+
+ @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);
+ }
+
+ @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);
+ }
+
+ @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);
+ }
+
+ @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);
+ }
+
+ @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);
+ }
+
+ @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));
+ }
+
+ @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));
+ }
+
+ @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
+ }
+ }
}
private List<SearchResult> getDummyDbResults() {
@@ -218,8 +521,8 @@
return results;
}
- private List<AppSearchResult> getDummyAppResults() {
- List<AppSearchResult> results = new ArrayList<>();
+ private List<SearchResult> getDummyAppResults() {
+ List<SearchResult> results = new ArrayList<>();
ResultPayload payload = new ResultPayload(new Intent());
AppSearchResult.Builder builder = new AppSearchResult.Builder();
builder.setPayload(payload)
@@ -265,4 +568,16 @@
sampleResults.add(builder.build());
return sampleResults;
}
-}
\ No newline at end of file
+
+ private List<Pair<String, Float>> getDummyRankingScores() {
+ List<SearchResult> results = getDummyDbResults();
+ List<Pair<String, Float>> scores = new ArrayList<>();
+ scores.add(
+ new Pair<String, Float>(Long.toString(results.get(2).stableId), 0.9f)); // charlie
+ scores.add(
+ new Pair<String, Float>(Long.toString(results.get(0).stableId), 0.8f)); // alpha
+ scores.add(
+ new Pair<String, Float>(Long.toString(results.get(1).stableId), 0.2f)); // bravo
+ return scores;
+ }
+}