Merge "Add optional feedback button for settings search"

This commit is contained in:
Matthew Fritze
2017-03-13 19:39:49 +00:00
committed by Android (Google) Code Review
11 changed files with 237 additions and 22 deletions

View File

@@ -0,0 +1,59 @@
<!--
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.
-->
<vector android:height="24dp" android:viewportHeight="267.9"
android:viewportWidth="236.3" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillAlpha="0.9" android:fillColor="#00000000"
android:pathData="M18,-19c0,-0.4 0,-0.5 -0.3,-0.3l-12,7c-0.3,0.2 -0.2,0.5 0.1,0.6l11.9,7C18,-4.5 18,-4.7 18,-5V-19z"
android:strokeColor="#FFFFFF" android:strokeWidth="2"/>
<path android:fillAlpha="0.9" android:fillColor="#00000000"
android:pathData="M18.9,-12c0,-3.8 -3.1,-6.9 -6.9,-6.9c-3.8,0 -6.9,3.1 -6.9,6.9c0,3.8 3.1,6.9 6.9,6.9C15.8,-5.1 18.9,-8.2 18.9,-12z"
android:strokeColor="#FFFFFF" android:strokeWidth="1.8033"/>
<path android:fillAlpha="0.9" android:fillColor="#00000000"
android:pathData="M19,-5.7C19,-5.4 18.6,-5 18.2,-5H5.8C5.4,-5 5,-5.4 5,-5.7v-12.6C5,-18.6 5.4,-19 5.8,-19h12.4c0.4,0 0.8,0.4 0.8,0.7V-5.7z"
android:strokeColor="#FFFFFF" android:strokeWidth="2"/>
<path android:fillColor="#FAFAFA" android:pathData="M118.2,134.4m-118.2,0a118.2,118.2 0,1 1,236.4 0a118.2,118.2 0,1 1,-236.4 0"/>
<path android:fillColor="#F5F5F5" android:pathData="M22.8,239L96,206.2c0.2,-0.1 0.3,-0.1 0.5,-0.1l117.3,23c0.7,0.2 0.9,1.2 0.2,1.5l-75.9,37.2c-0.2,0.1 -0.3,0.1 -0.5,0.1L23,240.5C22.2,240.3 22.1,239.3 22.8,239z"/>
<path android:fillColor="#E8E8E8" android:pathData="M35.6,76.5l102,17.3l0,167.4l-102,-23.2z"/>
<path android:fillColor="#E8E8E8" android:pathData="M35.7,19.6l102,17.3l0,79.2l-102,-23.2z"/>
<path android:fillColor="#EFEFEF" android:pathData="M35.7,19.6l66.6,-19.6l100.5,19.1l-65.1,18.5z"/>
<path android:fillColor="#00000000"
android:pathData="M37.6,131.9L135.2,150"
android:strokeColor="#E0E0E0" android:strokeWidth="0.1816"/>
<path android:fillAlpha="0.5" android:fillColor="#00000000"
android:pathData="M39.1,185.3L132.3,204.8"
android:strokeColor="#C1C1C1" android:strokeWidth="0.1816"/>
<path android:fillColor="#00000000"
android:pathData="M137.7,147.6L116.5,154.5"
android:strokeColor="#E0E0E0" android:strokeWidth="0.1816"/>
<path android:fillColor="#00000000" android:pathData="M35.7,142.2"
android:strokeColor="#E0E0E0" android:strokeWidth="0.1816"/>
<path android:fillAlpha="5.000000e-02" android:fillColor="#FF000000" android:pathData="M35.7,140.9l101.7,9.2l0,33.1l-101.8,-21.3z"/>
<path android:fillColor="#CECECE" android:pathData="M71,36.9l28.8,5l0,5.8l-28.8,-5.3z"/>
<path android:fillAlpha="0.1" android:fillColor="#1F1F1F" android:pathData="M71,36.9l28.8,5l0,1.7l-28.8,-5.3z"/>
<path android:fillAlpha="0.5" android:fillColor="#CECECE" android:pathData="M68.4,147.3l28.8,5.1l0,5.8l-28.8,-5.4z"/>
<path android:fillAlpha="0.1" android:fillColor="#1F1F1F" android:pathData="M68.4,147.3l28.8,5.1l0,1.7l-28.8,-5.4z"/>
<path android:fillAlpha="0.7" android:fillColor="#CECECE" android:pathData="M68.4,201.2l28.8,5.4l0,5.8l-28.8,-5.7z"/>
<path android:fillAlpha="0.1" android:fillColor="#1F1F1F" android:pathData="M68.4,201.2l28.8,5.4l0,1.6l-28.8,-5.6z"/>
<path android:fillColor="#EAEAEA" android:pathData="M16.2,80.3l100.3,18.1l0,58.5l-100.3,-20.2z"/>
<path android:fillColor="#EFEFEF" android:pathData="M137.7,37.6l0,53.9l-21.2,6.9l0,58.5l21.2,-6.9l0,111.1l65.1,-30l0,-212z"/>
<path android:fillColor="#E57373" android:pathData="M16.2,80.3l19.5,-8.2l0,11.7z"/>
<path android:fillAlpha="0.6" android:fillColor="#E57373" android:pathData="M137.7,91.5l-102,-19.4l0,11.7l80.8,14.6z"/>
<path android:fillColor="#CECECE" android:pathData="M47.2,95.5l28.8,5l0,5.8l-28.8,-5.2z"/>
<path android:fillAlpha="0.1" android:fillColor="#1F1F1F" android:pathData="M47.2,95.5l28.8,5l0,1.7l-28.8,-5.2z"/>
<path android:fillColor="#EAEAEA" android:pathData="M35.7,72.1L35.7,72.1l-19.6,8.1l0,0.1l0,0l100.3,18.1l0,0l0,0l21.6,-7L35.7,72.1zM116.5,97.5L19.4,80l16.4,-6.8L134,91.8L116.5,97.5z"/>
</vector>

View File

@@ -0,0 +1,23 @@
<?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.
-->
<View
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/feedback_popup"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="gone">
</View>

View File

@@ -13,18 +13,19 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout
android:id="@+id/search_panel" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:id="@+id/search_panel"
android:layout_height="match_parent" android:layout_width="match_parent"
android:layout_gravity="center" android:layout_height="match_parent"
android:orientation="vertical"> android:background="@drawable/search_panel_list_background">
<LinearLayout android:id="@+id/layout_recent_searches" <LinearLayout android:id="@+id/layout_recent_searches"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:orientation="vertical"> android:orientation="vertical"
android:layout_alignParentTop="true">
<!-- Padding is included in the background --> <!-- Padding is included in the background -->
<android.support.v7.widget.RecyclerView android:id="@+id/list_recent_searches" <android.support.v7.widget.RecyclerView android:id="@+id/list_recent_searches"
@@ -36,27 +37,47 @@
android:paddingBottom="@dimen/dashboard_padding_bottom" android:paddingBottom="@dimen/dashboard_padding_bottom"
android:scrollbarStyle="outsideOverlay" android:scrollbarStyle="outsideOverlay"
android:headerDividersEnabled="false" android:headerDividersEnabled="false"
android:background="@drawable/search_panel_list_background"
android:elevation="@dimen/search_panel_elevation"/> android:elevation="@dimen/search_panel_elevation"/>
</LinearLayout> </LinearLayout>
<LinearLayout android:id="@+id/layout_results" <LinearLayout android:id="@+id/layout_results"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="fill_parent"
android:layout_gravity="center" android:layout_above="@id/feedback_popup"
android:orientation="vertical"> android:orientation="vertical">
<!-- Padding is included in the background --> <!-- Padding is included in the background -->
<android.support.v7.widget.RecyclerView android:id="@+id/list_results" <android.support.v7.widget.RecyclerView android:id="@+id/list_results"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:paddingStart="@dimen/dashboard_padding_start" android:paddingStart="@dimen/dashboard_padding_start"
android:paddingEnd="@dimen/dashboard_padding_end" android:paddingEnd="@dimen/dashboard_padding_end"
android:paddingTop="@dimen/dashboard_padding_top" android:paddingTop="@dimen/dashboard_padding_top"
android:paddingBottom="@dimen/dashboard_padding_bottom" android:paddingBottom="@dimen/dashboard_padding_bottom"
android:scrollbarStyle="outsideOverlay" android:scrollbarStyle="outsideOverlay"
android:scrollbars="vertical" android:scrollbars="vertical"/>
android:background="@drawable/search_panel_list_background"/>
<LinearLayout
android:id="@+id/no_results_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:layout_height="112dp"
android:layout_width="112dp"
android:src="@drawable/no_search_results"/>
<TextView
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:textSize="18sp"
android:text="@string/search_settings_no_results"
android:gravity="center"/>
</LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout> <include layout="@layout/search_feedback"/>
</RelativeLayout>

View File

@@ -2167,6 +2167,8 @@
<string name="search_settings">Search</string> <string name="search_settings">Search</string>
<!-- Main Settings screen, setting option summary to go into search settings --> <!-- Main Settings screen, setting option summary to go into search settings -->
<string name="search_settings_summary">Manage search settings and history</string> <string name="search_settings_summary">Manage search settings and history</string>
<!-- There are no search results for the user's search [CHAR LIMIT=NONE]-->
<string name="search_settings_no_results">No results</string>
<!-- Display settings --><skip/> <!-- Display settings --><skip/>
<!-- Sound & display settings screen, section header for settings related to display --> <!-- Sound & display settings screen, section header for settings related to display -->

View File

@@ -21,6 +21,7 @@ import android.app.Fragment;
import android.app.FragmentManager; import android.app.FragmentManager;
import android.os.Bundle; import android.os.Bundle;
import android.view.WindowManager;
import com.android.settings.R; import com.android.settings.R;
public class SearchActivity extends Activity { public class SearchActivity extends Activity {
@@ -29,6 +30,8 @@ public class SearchActivity extends Activity {
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.search_main); setContentView(R.layout.search_main);
// Keeps layouts in-place when keyboard opens.
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
FragmentManager fragmentManager = getFragmentManager(); FragmentManager fragmentManager = getFragmentManager();
Fragment fragment = fragmentManager.findFragmentById(R.id.main_content); Fragment fragment = fragmentManager.findFragmentById(R.id.main_content);

View File

@@ -19,6 +19,7 @@ import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.view.Menu; import android.view.Menu;
import android.view.View;
import com.android.settings.dashboard.SiteMapManager; import com.android.settings.dashboard.SiteMapManager;
/** /**
@@ -68,4 +69,25 @@ public interface SearchFeatureProvider {
* Updates the Settings indexes * Updates the Settings indexes
*/ */
void updateIndex(Context context); void updateIndex(Context context);
/**
* Initializes the feedback button in case it was dismissed.
*/
default void initFeedbackButton() {
}
/**
* Show a button users can click to submit feedback on the quality of the search results.
*/
default void showFeedbackButton(SearchFragment fragment, View view) {
}
/**
* Hide the feedback button shown by
* {@link #showFeedbackButton(SearchFragment fragment, View view) showFeedbackButton}
*/
default void hideFeedbackButton() {
}
} }

View File

@@ -31,6 +31,7 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams; import android.widget.LinearLayout.LayoutParams;
import android.widget.SearchView; import android.widget.SearchView;
@@ -71,7 +72,7 @@ public class SearchFragment extends InstrumentedFragment implements
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
static final String RESULT_CLICK_COUNT = "settings_search_result_click_count"; static final String RESULT_CLICK_COUNT = "settings_search_result_click_count";
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @VisibleForTesting
String mQuery; String mQuery;
private final SaveQueryRecorderCallback mSaveQueryRecorderCallback = private final SaveQueryRecorderCallback mSaveQueryRecorderCallback =
@@ -89,6 +90,7 @@ public class SearchFragment extends InstrumentedFragment implements
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
RecyclerView mResultsRecyclerView; RecyclerView mResultsRecyclerView;
private SearchView mSearchView; private SearchView mSearchView;
private LinearLayout mNoResultsView;
@VisibleForTesting @VisibleForTesting
final RecyclerView.OnScrollListener mScrollListener = final RecyclerView.OnScrollListener mScrollListener =
@@ -119,6 +121,8 @@ public class SearchFragment extends InstrumentedFragment implements
setHasOptionsMenu(true); setHasOptionsMenu(true);
mSearchAdapter = new SearchResultsAdapter(this); mSearchAdapter = new SearchResultsAdapter(this);
mSearchFeatureProvider.initFeedbackButton();
final LoaderManager loaderManager = getLoaderManager(); final LoaderManager loaderManager = getLoaderManager();
if (savedInstanceState != null) { if (savedInstanceState != null) {
@@ -155,6 +159,8 @@ public class SearchFragment extends InstrumentedFragment implements
mResultsRecyclerView.setAdapter(mSearchAdapter); mResultsRecyclerView.setAdapter(mSearchAdapter);
mResultsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); mResultsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mResultsRecyclerView.addOnScrollListener(mScrollListener); mResultsRecyclerView.addOnScrollListener(mScrollListener);
mNoResultsView = (LinearLayout) view.findViewById(R.id.no_results_layout);
return view; return view;
} }
@@ -184,16 +190,26 @@ public class SearchFragment extends InstrumentedFragment implements
if (TextUtils.equals(query, mQuery)) { if (TextUtils.equals(query, mQuery)) {
return true; return true;
} }
final boolean isEmptyQuery = TextUtils.isEmpty(query);
// Hide no-results-view when the new query is not a super-string of the previous
if ((mQuery != null) && (mNoResultsView.getVisibility() == View.VISIBLE)
&& (query.length() < mQuery.length())) {
mNoResultsView.setVisibility(View.GONE);
}
mResultClickCount = 0; mResultClickCount = 0;
mNeverEnteredQuery = false; mNeverEnteredQuery = false;
mQuery = query; mQuery = query;
mSearchAdapter.clearResults(); mSearchAdapter.clearResults();
if (TextUtils.isEmpty(mQuery)) { if (isEmptyQuery) {
final LoaderManager loaderManager = getLoaderManager(); final LoaderManager loaderManager = getLoaderManager();
loaderManager.destroyLoader(LOADER_ID_DATABASE); loaderManager.destroyLoader(LOADER_ID_DATABASE);
loaderManager.destroyLoader(LOADER_ID_INSTALLED_APPS); loaderManager.destroyLoader(LOADER_ID_INSTALLED_APPS);
loaderManager.restartLoader(LOADER_ID_RECENTS, null /* args */, this /* callback */); loaderManager.restartLoader(LOADER_ID_RECENTS, null /* args */, this /* callback */);
mSearchFeatureProvider.hideFeedbackButton();
} else { } else {
restartLoaders(); restartLoaders();
} }
@@ -232,7 +248,12 @@ public class SearchFragment extends InstrumentedFragment implements
mSearchAdapter.addResultsToMap(data, loader.getClass().getName()); mSearchAdapter.addResultsToMap(data, loader.getClass().getName());
if (mUnfinishedLoadersCount.decrementAndGet() == 0) { if (mUnfinishedLoadersCount.decrementAndGet() == 0) {
mSearchAdapter.mergeResults(); final int resultCount = mSearchAdapter.mergeResults();
mSearchFeatureProvider.showFeedbackButton(this, getView());
if (resultCount == 0) {
mNoResultsView.setVisibility(View.VISIBLE);
}
} }
} }
@@ -257,6 +278,14 @@ public class SearchFragment extends InstrumentedFragment implements
loaderManager.restartLoader(LOADER_ID_INSTALLED_APPS, null /* args */, this /* callback */); loaderManager.restartLoader(LOADER_ID_INSTALLED_APPS, null /* args */, this /* callback */);
} }
public String getQuery() {
return mQuery;
}
public List<SearchResult> getSearchResults() {
return mSearchAdapter.getSearchResults();
}
@VisibleForTesting (otherwise = VisibleForTesting.PRIVATE) @VisibleForTesting (otherwise = VisibleForTesting.PRIVATE)
SearchView makeSearchView(ActionBar actionBar, String query) { SearchView makeSearchView(ActionBar actionBar, String query) {
final SearchView searchView = new SearchView(actionBar.getThemedContext()); final SearchView searchView = new SearchView(actionBar.getThemedContext());

View File

@@ -106,8 +106,10 @@ public class SearchResultsAdapter extends Adapter<SearchViewHolder> {
/** /**
* Merge the results from each of the loaders into one list for the adapter. * Merge the results from each of the loaders into one list for the adapter.
* Prioritizes results from the local database over installed apps. * Prioritizes results from the local database over installed apps.
*
* @return Number of matched results
*/ */
public void mergeResults() { public int mergeResults() {
final List<? extends SearchResult> databaseResults = mResultsMap final List<? extends SearchResult> databaseResults = mResultsMap
.get(DatabaseResultLoader.class.getName()); .get(DatabaseResultLoader.class.getName());
final List<? extends SearchResult> installedAppResults = mResultsMap final List<? extends SearchResult> installedAppResults = mResultsMap
@@ -139,6 +141,8 @@ public class SearchResultsAdapter extends Adapter<SearchViewHolder> {
mSearchResults.addAll(results); mSearchResults.addAll(results);
notifyDataSetChanged(); notifyDataSetChanged();
return mSearchResults.size();
} }
public void clearResults() { public void clearResults() {

View File

@@ -60,7 +60,7 @@ import static org.mockito.Mockito.doReturn;
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class SearchAdapterTest { public class SearchResultsAdapterTest {
@Mock @Mock
private SearchFragment mFragment; private SearchFragment mFragment;
@@ -114,7 +114,7 @@ public class SearchAdapterTest {
InstalledAppResultLoader.class.getName()); InstalledAppResultLoader.class.getName());
mAdapter.addResultsToMap(getDummyDbResults(), mAdapter.addResultsToMap(getDummyDbResults(),
DatabaseResultLoader.class.getName()); DatabaseResultLoader.class.getName());
mAdapter.mergeResults(); int count = mAdapter.mergeResults();
List<SearchResult> results = mAdapter.getSearchResults(); List<SearchResult> results = mAdapter.getSearchResults();
assertThat(results.get(0).title).isEqualTo("alpha"); assertThat(results.get(0).title).isEqualTo("alpha");
@@ -123,6 +123,7 @@ public class SearchAdapterTest {
assertThat(results.get(3).title).isEqualTo("bravo"); assertThat(results.get(3).title).isEqualTo("bravo");
assertThat(results.get(4).title).isEqualTo("appCharlie"); assertThat(results.get(4).title).isEqualTo("appCharlie");
assertThat(results.get(5).title).isEqualTo("Charlie"); assertThat(results.get(5).title).isEqualTo("Charlie");
assertThat(count).isEqualTo(6);
} }
private List<SearchResult> getDummyDbResults() { private List<SearchResult> getDummyDbResults() {

View File

@@ -67,7 +67,6 @@ public class DatabaseResultLoaderTest {
private final String summaryOne = "summaryOne"; private final String summaryOne = "summaryOne";
private final String summaryTwo = "summaryTwo"; private final String summaryTwo = "summaryTwo";
private final String summaryThree = "summaryThree"; private final String summaryThree = "summaryThree";
private final String summaryFour = "summaryFour";
SQLiteDatabase mDb; SQLiteDatabase mDb;

View File

@@ -16,10 +16,13 @@
package com.android.settings.search2; package com.android.settings.search2;
import android.app.LoaderManager;
import android.content.Context; import android.content.Context;
import android.content.Loader; import android.content.Loader;
import android.os.Bundle; import android.os.Bundle;
import android.view.View;
import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.SettingsRobolectricTestRunner;
@@ -42,6 +45,7 @@ import java.util.List;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
@@ -241,4 +245,52 @@ public class SearchFragmentTest {
verify(fragment, times(2)).onLoadFinished(any(Loader.class), any(List.class)); verify(fragment, times(2)).onLoadFinished(any(Loader.class), any(List.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));
ActivityController<SearchActivity> activityController =
Robolectric.buildActivity(SearchActivity.class);
activityController.setup();
SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager()
.findFragmentById(R.id.main_content));
when(fragment.getLoaderManager()).thenReturn(mock(LoaderManager.class));
fragment.onQueryTextChange("");
Robolectric.flushForegroundThreadScheduler();
verify(mFeatureFactory.searchFeatureProvider).hideFeedbackButton();
}
@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));
ActivityController<SearchActivity> activityController =
Robolectric.buildActivity(SearchActivity.class);
activityController.setup();
SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager()
.findFragmentById(R.id.main_content));
fragment.onQueryTextChange("non-empty");
Robolectric.flushForegroundThreadScheduler();
verify(mFeatureFactory.searchFeatureProvider).showFeedbackButton(any(SearchFragment.class),
any(View.class));
}
} }