OSDN Git Service

Display recents when search fragment open.
authorFan Zhang <zhfan@google.com>
Tue, 14 Mar 2017 01:09:29 +0000 (18:09 -0700)
committerFan Zhang <zhfan@google.com>
Tue, 14 Mar 2017 18:59:57 +0000 (11:59 -0700)
Bug: 27391895
Test: make RunSettingsRoboTests
Change-Id: I62cfa35af96babf85a795910aa76dc774e2b80f2

src/com/android/settings/search2/SavedQueryViewHolder.java
src/com/android/settings/search2/SearchFragment.java
src/com/android/settings/search2/SearchResultsAdapter.java
tests/robotests/src/com/android/settings/search/SearchResultsAdapterTest.java
tests/robotests/src/com/android/settings/search2/SavedQueryViewHolderTest.java [new file with mode: 0644]
tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java

index a32ed05..15d6f8b 100644 (file)
@@ -25,7 +25,7 @@ public class SavedQueryViewHolder extends SearchViewHolder {
 
     public SavedQueryViewHolder(View view) {
         super(view);
-        titleView = (TextView) view.findViewById(android.R.id.title);
+        titleView = view.findViewById(android.R.id.title);
     }
 
     @Override
index 957713b..7eb9698 100644 (file)
@@ -45,9 +45,8 @@ import com.android.settings.overlay.FeatureFactory;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
-public class SearchFragment extends InstrumentedFragment implements
-        SearchView.OnQueryTextListener, LoaderManager.LoaderCallbacks<List<? extends SearchResult>>
-{
+public class SearchFragment extends InstrumentedFragment implements SearchView.OnQueryTextListener,
+        LoaderManager.LoaderCallbacks<List<? extends SearchResult>> {
     private static final String TAG = "SearchFragment";
 
     @VisibleForTesting
@@ -66,7 +65,7 @@ public class SearchFragment extends InstrumentedFragment implements
     private static final int NUM_QUERY_LOADERS = 2;
 
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    AtomicInteger mUnfinishedLoadersCount = new AtomicInteger(NUM_QUERY_LOADERS);;
+    AtomicInteger mUnfinishedLoadersCount = new AtomicInteger(NUM_QUERY_LOADERS);
 
     // Logging
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
@@ -82,7 +81,7 @@ public class SearchFragment extends InstrumentedFragment implements
     private int mResultClickCount;
     private MetricsFeatureProvider mMetricsFeatureProvider;
 
-    @VisibleForTesting (otherwise = VisibleForTesting.PRIVATE)
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     SearchFeatureProvider mSearchFeatureProvider;
 
     private SearchResultsAdapter mSearchAdapter;
@@ -93,8 +92,7 @@ public class SearchFragment extends InstrumentedFragment implements
     private LinearLayout mNoResultsView;
 
     @VisibleForTesting
-    final RecyclerView.OnScrollListener mScrollListener =
-            new RecyclerView.OnScrollListener() {
+    final RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() {
         @Override
         public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
             if (dy != 0) {
@@ -120,7 +118,7 @@ public class SearchFragment extends InstrumentedFragment implements
         super.onCreate(savedInstanceState);
         setHasOptionsMenu(true);
         mSearchAdapter = new SearchResultsAdapter(this);
-        
+
         mSearchFeatureProvider.initFeedbackButton();
 
         final LoaderManager loaderManager = getLoaderManager();
@@ -155,12 +153,12 @@ public class SearchFragment extends InstrumentedFragment implements
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
         final View view = inflater.inflate(R.layout.search_panel_2, container, false);
-        mResultsRecyclerView = (RecyclerView) view.findViewById(R.id.list_results);
+        mResultsRecyclerView = view.findViewById(R.id.list_results);
         mResultsRecyclerView.setAdapter(mSearchAdapter);
         mResultsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
         mResultsRecyclerView.addOnScrollListener(mScrollListener);
 
-        mNoResultsView = (LinearLayout) view.findViewById(R.id.no_results_layout);
+        mNoResultsView = view.findViewById(R.id.no_results_layout);
         return view;
     }
 
@@ -245,16 +243,20 @@ public class SearchFragment extends InstrumentedFragment implements
     @Override
     public void onLoadFinished(Loader<List<? extends SearchResult>> loader,
             List<? extends SearchResult> data) {
-        mSearchAdapter.addResultsToMap(data, loader.getClass().getName());
-
-        if (mUnfinishedLoadersCount.decrementAndGet() == 0) {
-            final int resultCount = mSearchAdapter.mergeResults();
-            mSearchFeatureProvider.showFeedbackButton(this, getView());
-
-            if (resultCount == 0) {
-                mNoResultsView.setVisibility(View.VISIBLE);
-            }
+        final int resultCount;
+        switch (loader.getId()) {
+            case LOADER_ID_RECENTS:
+                resultCount = mSearchAdapter.displaySavedQuery(data);
+                break;
+            default:
+                mSearchAdapter.addSearchResults(data, loader.getClass().getName());
+                if (mUnfinishedLoadersCount.decrementAndGet() != 0) {
+                    return;
+                }
+                resultCount = mSearchAdapter.displaySearchResults();
         }
+        mNoResultsView.setVisibility(resultCount == 0 ? View.VISIBLE : View.GONE);
+        mSearchFeatureProvider.showFeedbackButton(this, getView());
     }
 
     @Override
@@ -267,6 +269,8 @@ public class SearchFragment extends InstrumentedFragment implements
 
     public void onSavedQueryClicked(CharSequence query) {
         final String queryString = query.toString();
+        mMetricsFeatureProvider.action(getContext(),
+                MetricsProto.MetricsEvent.ACTION_CLICK_SETTINGS_SEARCH_SAVED_QUERY);
         mSearchView.setQuery(queryString, false /* submit */);
         onQueryTextChange(queryString);
     }
@@ -286,7 +290,7 @@ public class SearchFragment extends InstrumentedFragment implements
         return mSearchAdapter.getSearchResults();
     }
 
-    @VisibleForTesting (otherwise = VisibleForTesting.PRIVATE)
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
     SearchView makeSearchView(ActionBar actionBar, String query) {
         final SearchView searchView = new SearchView(actionBar.getThemedContext());
         searchView.setIconifiedByDefault(false);
index b76958a..6ff68b1 100644 (file)
@@ -19,7 +19,7 @@ package com.android.settings.search2;
 import android.content.Context;
 import android.support.annotation.MainThread;
 import android.support.annotation.VisibleForTesting;
-import android.support.v7.widget.RecyclerView.Adapter;
+import android.support.v7.widget.RecyclerView;
 import android.util.ArrayMap;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -32,10 +32,10 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
-import static com.android.settings.search2.SearchResult.TOP_RANK;
 import static com.android.settings.search2.SearchResult.BOTTOM_RANK;
+import static com.android.settings.search2.SearchResult.TOP_RANK;
 
-public class SearchResultsAdapter extends Adapter<SearchViewHolder> {
+public class SearchResultsAdapter extends RecyclerView.Adapter<SearchViewHolder> {
 
     private final List<SearchResult> mSearchResults;
     private final SearchFragment mFragment;
@@ -91,16 +91,28 @@ public class SearchResultsAdapter extends Adapter<SearchViewHolder> {
 
     /**
      * Store the results from each of the loaders to be merged when all loaders are finished.
-     * @param freshResults are the results from the loader.
+     *
+     * @param results         the results from the loader.
      * @param loaderClassName class name of the loader.
      */
     @MainThread
-    public void addResultsToMap(List<? extends SearchResult> freshResults,
-            String loaderClassName) {
-        if (freshResults == null) {
+    public void addSearchResults(List<? extends SearchResult> results, String loaderClassName) {
+        if (results == null) {
             return;
         }
-        mResultsMap.put(loaderClassName, freshResults);
+        mResultsMap.put(loaderClassName, results);
+    }
+
+    /**
+     * Displays recent searched queries.
+     *
+     * @return The number of saved queries to display
+     */
+    public int displaySavedQuery(List<? extends SearchResult> data) {
+        clearResults();
+        mSearchResults.addAll(data);
+        notifyDataSetChanged();
+        return mSearchResults.size();
     }
 
     /**
@@ -109,7 +121,7 @@ public class SearchResultsAdapter extends Adapter<SearchViewHolder> {
      *
      * @return Number of matched results
      */
-    public int mergeResults() {
+    public int displaySearchResults() {
         final List<? extends SearchResult> databaseResults = mResultsMap
                 .get(DatabaseResultLoader.class.getName());
         final List<? extends SearchResult> installedAppResults = mResultsMap
@@ -129,7 +141,7 @@ public class SearchResultsAdapter extends Adapter<SearchViewHolder> {
             while ((appIndex < appSize) && (installedAppResults.get(appIndex).rank == rank)) {
                 results.add(installedAppResults.get(appIndex++));
             }
-            rank ++;
+            rank++;
         }
 
         while (dbIndex < dbSize) {
index 6100050..647d68c 100644 (file)
@@ -21,9 +21,9 @@ import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
-
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
+
 import com.android.settings.R;
 import com.android.settings.SettingsRobolectricTestRunner;
 import com.android.settings.TestConfig;
@@ -34,13 +34,12 @@ import com.android.settings.search2.InstalledAppResultLoader;
 import com.android.settings.search2.IntentPayload;
 import com.android.settings.search2.IntentSearchViewHolder;
 import com.android.settings.search2.ResultPayload;
-import com.android.settings.search2.SearchActivity;
 import com.android.settings.search2.SearchFragment;
 import com.android.settings.search2.SearchResult;
 import com.android.settings.search2.SearchResult.Builder;
 import com.android.settings.search2.SearchResultsAdapter;
-
 import com.android.settings.search2.SearchViewHolder;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -48,15 +47,11 @@ import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.Robolectric;
 import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowApplication;
-import org.robolectric.shadows.ShadowViewGroup;
-import org.robolectric.util.ActivityController;
 
 import java.util.ArrayList;
 import java.util.List;
 
 import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.doReturn;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -85,15 +80,15 @@ public class SearchResultsAdapterTest {
     @Test
     public void testSingleSourceMerge_ExactCopyReturned() {
         ArrayList<SearchResult> intentResults = getIntentSampleResults();
-        mAdapter.addResultsToMap(intentResults, mLoaderClassName);
-        mAdapter.mergeResults();
+        mAdapter.addSearchResults(intentResults, mLoaderClassName);
+        mAdapter.displaySearchResults();
 
         List<SearchResult> updatedResults = mAdapter.getSearchResults();
         assertThat(updatedResults).containsAllIn(intentResults);
     }
 
     @Test
-    public void testCreatViewHolder_ReturnsIntentResult() {
+    public void testCreateViewHolder_ReturnsIntentResult() {
         ViewGroup group = new FrameLayout(mContext);
         SearchViewHolder view = mAdapter.onCreateViewHolder(group,
                 ResultPayload.PayloadType.INTENT);
@@ -101,7 +96,7 @@ public class SearchResultsAdapterTest {
     }
 
     @Test
-    public void testCreatViewHolder_ReturnsInlineSwitchResult() {
+    public void testCreateViewHolder_ReturnsInlineSwitchResult() {
         ViewGroup group = new FrameLayout(mContext);
         SearchViewHolder view = mAdapter.onCreateViewHolder(group,
                 ResultPayload.PayloadType.INLINE_SWITCH);
@@ -110,11 +105,11 @@ public class SearchResultsAdapterTest {
 
     @Test
     public void testEndToEndSearch_ProperResultsMerged() {
-        mAdapter.addResultsToMap(getDummyAppResults(),
+        mAdapter.addSearchResults(getDummyAppResults(),
                 InstalledAppResultLoader.class.getName());
-        mAdapter.addResultsToMap(getDummyDbResults(),
+        mAdapter.addSearchResults(getDummyDbResults(),
                 DatabaseResultLoader.class.getName());
-        int count = mAdapter.mergeResults();
+        int count = mAdapter.displaySearchResults();
 
         List<SearchResult> results = mAdapter.getSearchResults();
         assertThat(results.get(0).title).isEqualTo("alpha");
diff --git a/tests/robotests/src/com/android/settings/search2/SavedQueryViewHolderTest.java b/tests/robotests/src/com/android/settings/search2/SavedQueryViewHolderTest.java
new file mode 100644 (file)
index 0000000..cc3cd33
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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.search2;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class SavedQueryViewHolderTest {
+
+    @Mock
+    private SearchFragment mSearchFragment;
+    private Context mContext;
+    private SavedQueryViewHolder mHolder;
+    private View mView;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mView = LayoutInflater.from(mContext)
+                .inflate(R.layout.search_saved_query_item, null);
+        mHolder = new SavedQueryViewHolder(mView);
+    }
+
+    @Test
+    public void onBind_shouldBindClickCallback() {
+        final SearchResult result = mock(SearchResult.class);
+        mHolder.onBind(mSearchFragment, result);
+
+        mView.performClick();
+
+        verify(mSearchFragment).onSavedQueryClicked(any(CharSequence.class));
+    }
+}
index 6a61f52..e7565c7 100644 (file)
 package com.android.settings.search2;
 
 import android.app.LoaderManager;
-
 import android.content.Context;
 import android.content.Loader;
 import android.os.Bundle;
-
 import android.view.View;
+
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.R;
 import com.android.settings.SettingsRobolectricTestRunner;
@@ -39,10 +38,12 @@ import org.robolectric.Robolectric;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.util.ActivityController;
+import org.robolectric.util.ReflectionHelpers;
 
 import java.util.List;
 
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyList;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
@@ -186,20 +187,23 @@ public class SearchFragmentTest {
         ActivityController<SearchActivity> activityController =
                 Robolectric.buildActivity(SearchActivity.class);
         activityController.setup();
-        SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager()
-                .findFragmentById(R.id.main_content);
 
-        fragment.onQueryTextChange("");
-        activityController.get().onBackPressed();
-        activityController.pause().stop().destroy();
+        SearchFragment fragment = spy((SearchFragment) activityController.get().getFragmentManager()
+                .findFragmentById(R.id.main_content));
+
+        final SearchResultsAdapter adapter = mock(SearchResultsAdapter.class);
+        ReflectionHelpers.setField(fragment, "mSearchAdapter", adapter);
 
         verify(mFeatureFactory.searchFeatureProvider, never())
                 .getDatabaseSearchLoader(any(Context.class), anyString());
         verify(mFeatureFactory.searchFeatureProvider, never())
                 .getInstalledAppSearchLoader(any(Context.class), anyString());
-        // Saved query loaded 2 times: fragment start, and query change to empty.
-        verify(mFeatureFactory.searchFeatureProvider, times(2))
+        verify(mFeatureFactory.searchFeatureProvider)
                 .getSavedQueryLoader(any(Context.class));
+
+        fragment.onLoadFinished(mSavedQueryLoader, null /* data */);
+
+        verify(adapter).displaySavedQuery(anyList());
     }
 
     @Test
@@ -225,7 +229,6 @@ public class SearchFragmentTest {
 
     @Test
     public void syncLoaders_MergeWhenAllLoadersDone() {
-
         when(mFeatureFactory.searchFeatureProvider
                 .getDatabaseSearchLoader(any(Context.class), anyString()))
                 .thenReturn(new MockDBLoader(RuntimeEnvironment.application));
@@ -236,6 +239,7 @@ public class SearchFragmentTest {
         ActivityController<SearchActivity> activityController =
                 Robolectric.buildActivity(SearchActivity.class);
         activityController.setup();
+
         SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager()
                 .findFragmentById(R.id.main_content));