diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java index 34990ec9479..3e45af7eeb9 100644 --- a/src/com/android/settings/SettingsActivity.java +++ b/src/com/android/settings/SettingsActivity.java @@ -79,7 +79,7 @@ public class SettingsActivity extends SettingsDrawerActivity private static final String LOG_TAG = "Settings"; - private static final int LOADER_ID_INDEXABLE_CONTENT_MONITOR = 1; + public static final int LOADER_ID_INDEXABLE_CONTENT_MONITOR = 1; // Constants for state save/restore private static final String SAVE_KEY_CATEGORIES = ":settings:categories"; diff --git a/src/com/android/settings/search/IndexingCallback.java b/src/com/android/settings/search/IndexingCallback.java new file mode 100644 index 00000000000..b4b6eabb393 --- /dev/null +++ b/src/com/android/settings/search/IndexingCallback.java @@ -0,0 +1,12 @@ +package com.android.settings.search; + +/** + * Callback for Settings search indexing. + */ +public interface IndexingCallback { + + /** + * Called when Indexing is finished. + */ + void onIndexingFinished(); +} diff --git a/src/com/android/settings/search2/DatabaseIndexingManager.java b/src/com/android/settings/search2/DatabaseIndexingManager.java index 80ae54fd7ae..c627ea56094 100644 --- a/src/com/android/settings/search2/DatabaseIndexingManager.java +++ b/src/com/android/settings/search2/DatabaseIndexingManager.java @@ -42,6 +42,7 @@ import android.util.Xml; import com.android.settings.core.PreferenceController; import com.android.settings.search.IndexDatabaseHelper; import com.android.settings.search.Indexable; +import com.android.settings.search.IndexingCallback; import com.android.settings.search.SearchIndexableRaw; import com.android.settings.search.SearchIndexableResources; @@ -132,7 +133,8 @@ public class DatabaseIndexingManager { private final String mBaseAuthority; - private final AtomicBoolean mIsAvailable = new AtomicBoolean(false); + @VisibleForTesting + final AtomicBoolean mIsIndexingComplete = new AtomicBoolean(false); @VisibleForTesting final UpdateData mDataToProcess = new UpdateData(); @@ -147,17 +149,13 @@ public class DatabaseIndexingManager { mContext = context; } - public boolean isAvailable() { - return mIsAvailable.get(); + public boolean isIndexingComplete() { + return mIsIndexingComplete.get(); } - public void indexDatabase() { - AsyncTask.execute(new Runnable() { - @Override - public void run() { - performIndexing(); - } - }); + public void indexDatabase(IndexingCallback callback) { + IndexingTask task = new IndexingTask(callback); + task.execute(); } /** @@ -171,15 +169,12 @@ public class DatabaseIndexingManager { final List list = mContext.getPackageManager().queryIntentContentProviders(intent, 0); - final String localeStr = Locale.getDefault().toString(); - final String fingerprint = Build.FINGERPRINT; + String localeStr = Locale.getDefault().toString(); + String fingerprint = Build.FINGERPRINT; final boolean isFullIndex = isFullIndex(localeStr, fingerprint); - // Drop the database when the locale or build has changed. This eliminates rows which are - // dynamically inserted in the old language, or deprecated settings. if (isFullIndex) { - final SQLiteDatabase db = getWritableDatabase(); - IndexDatabaseHelper.getInstance(mContext).reconstruct(db); + rebuildDatabase(); } for (final ResolveInfo info : list) { @@ -217,6 +212,18 @@ public class DatabaseIndexingManager { return !isLocaleIndexed || !isBuildIndexed; } + /** + * Reconstruct the database in the following cases: + * - Language has changed + * - Build has changed + */ + private void rebuildDatabase() { + // Drop the database when the locale or build has changed. This eliminates rows which are + // dynamically inserted in the old language, or deprecated settings. + final SQLiteDatabase db = getWritableDatabase(); + IndexDatabaseHelper.getInstance(mContext).reconstruct(db); + } + /** * Adds new data to the database and verifies the correctness of the ENABLED column. * First, the data to be updated and all non-indexable keys are copied locally. @@ -229,7 +236,6 @@ public class DatabaseIndexingManager { */ @VisibleForTesting void updateDatabase(boolean needsReindexing, String localeStr) { - mIsAvailable.set(false); final UpdateData copy; synchronized (mDataToProcess) { @@ -264,8 +270,6 @@ public class DatabaseIndexingManager { } finally { database.endTransaction(); } - - mIsAvailable.set(true); } /** @@ -1223,4 +1227,33 @@ public class DatabaseIndexingManager { } } } + + public class IndexingTask extends AsyncTask { + + @VisibleForTesting + IndexingCallback mCallback; + + public IndexingTask(IndexingCallback callback) { + mCallback = callback; + } + + @Override + protected void onPreExecute() { + mIsIndexingComplete.set(false); + } + + @Override + protected Void doInBackground(Void... voids) { + performIndexing(); + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + mIsIndexingComplete.set(true); + if (mCallback != null) { + mCallback.onIndexingFinished(); + } + } + } } \ No newline at end of file diff --git a/src/com/android/settings/search2/SearchFeatureProvider.java b/src/com/android/settings/search2/SearchFeatureProvider.java index d3dc24b026f..fa052fc4f0b 100644 --- a/src/com/android/settings/search2/SearchFeatureProvider.java +++ b/src/com/android/settings/search2/SearchFeatureProvider.java @@ -21,6 +21,7 @@ import android.view.Menu; import android.view.View; import com.android.settings.dashboard.SiteMapManager; +import com.android.settings.search.IndexingCallback; /** * FeatureProvider for Settings Search @@ -68,7 +69,12 @@ public interface SearchFeatureProvider { /** * Updates the Settings indexes */ - void updateIndex(Context context); + void updateIndex(Context context, IndexingCallback callback); + + /** + * @returns true when indexing is complete. + */ + boolean isIndexingComplete(Context context); /** * Initializes the feedback button in case it was dismissed. diff --git a/src/com/android/settings/search2/SearchFeatureProviderImpl.java b/src/com/android/settings/search2/SearchFeatureProviderImpl.java index b180c982679..4e47f9db57a 100644 --- a/src/com/android/settings/search2/SearchFeatureProviderImpl.java +++ b/src/com/android/settings/search2/SearchFeatureProviderImpl.java @@ -19,6 +19,7 @@ package com.android.settings.search2; import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.support.annotation.VisibleForTesting; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -26,6 +27,9 @@ import android.view.MenuItem; import com.android.settings.R; import com.android.settings.applications.PackageManagerWrapperImpl; import com.android.settings.dashboard.SiteMapManager; +import com.android.settings.search.IndexingCallback; + +import java.util.concurrent.atomic.AtomicBoolean; /** * FeatureProvider for the refactored search code. @@ -88,6 +92,11 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider { return mDatabaseIndexingManager; } + @Override + public boolean isIndexingComplete(Context context) { + return getIndexingManager(context).isIndexingComplete(); + } + public SiteMapManager getSiteMapManager() { if (mSiteMapManager == null) { mSiteMapManager = new SiteMapManager(); @@ -96,9 +105,9 @@ public class SearchFeatureProviderImpl implements SearchFeatureProvider { } @Override - public void updateIndex(Context context) { + public void updateIndex(Context context, IndexingCallback callback) { long indexStartTime = System.currentTimeMillis(); - getIndexingManager(context).indexDatabase(); + getIndexingManager(context).indexDatabase(callback); Log.d(TAG, "IndexDatabase() took " + (System.currentTimeMillis() - indexStartTime) + " ms"); } diff --git a/src/com/android/settings/search2/SearchFragment.java b/src/com/android/settings/search2/SearchFragment.java index c5db08d969f..5ee22b57f28 100644 --- a/src/com/android/settings/search2/SearchFragment.java +++ b/src/com/android/settings/search2/SearchFragment.java @@ -41,12 +41,23 @@ import com.android.settings.Utils; import com.android.settings.core.InstrumentedFragment; import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.overlay.FeatureFactory; +import com.android.settings.search.IndexingCallback; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +/** + * This fragment manages the lifecycle of indexing and searching. + * + * In onCreate, the indexing process is initiated in DatabaseIndexingManager. + * While the indexing is happening, loaders are blocked from accessing the database, but the user + * is free to start typing their query. + * + * When the indexing is complete, the fragment gets a callback to initialize the loaders and search + * the query if the user has entered text. + */ public class SearchFragment extends InstrumentedFragment implements SearchView.OnQueryTextListener, - LoaderManager.LoaderCallbacks> { + LoaderManager.LoaderCallbacks>, IndexingCallback { private static final String TAG = "SearchFragment"; @VisibleForTesting @@ -59,26 +70,30 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O private static final String STATE_RESULT_CLICK_COUNT = "state_result_click_count"; // Loader IDs - private static final int LOADER_ID_DATABASE = 1; - private static final int LOADER_ID_INSTALLED_APPS = 2; + @VisibleForTesting + static final int LOADER_ID_DATABASE = 1; + @VisibleForTesting + static final int LOADER_ID_INSTALLED_APPS = 2; private static final int NUM_QUERY_LOADERS = 2; - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + @VisibleForTesting AtomicInteger mUnfinishedLoadersCount = new AtomicInteger(NUM_QUERY_LOADERS); // Logging - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + @VisibleForTesting static final String RESULT_CLICK_COUNT = "settings_search_result_click_count"; @VisibleForTesting String mQuery; private boolean mNeverEnteredQuery = true; - private boolean mShowingSavedQuery; + @VisibleForTesting + boolean mShowingSavedQuery; private int mResultClickCount; private MetricsFeatureProvider mMetricsFeatureProvider; - private SavedQueryController mSavedQueryController; + @VisibleForTesting + SavedQueryController mSavedQueryController; @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) SearchFeatureProvider mSearchFeatureProvider; @@ -87,7 +102,8 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) RecyclerView mResultsRecyclerView; - private SearchView mSearchView; + @VisibleForTesting + SearchView mSearchView; private LinearLayout mNoResultsView; @VisibleForTesting @@ -116,6 +132,7 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); + final LoaderManager loaderManager = getLoaderManager(); mSearchAdapter = new SearchResultsAdapter(this); mSavedQueryController = new SavedQueryController( @@ -127,15 +144,8 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O mNeverEnteredQuery = savedInstanceState.getBoolean(STATE_NEVER_ENTERED_QUERY); mResultClickCount = savedInstanceState.getInt(STATE_RESULT_CLICK_COUNT); mShowingSavedQuery = savedInstanceState.getBoolean(STATE_SHOWING_SAVED_QUERY); - if (mShowingSavedQuery) { - mSavedQueryController.loadSavedQueries(); - } else { - loaderManager.initLoader(LOADER_ID_DATABASE, null, this); - loaderManager.initLoader(LOADER_ID_INSTALLED_APPS, null, this); - } } else { mShowingSavedQuery = true; - mSavedQueryController.loadSavedQueries(); } final Activity activity = getActivity(); @@ -148,7 +158,7 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O // Run the Index update only if we have some space if (!Utils.isLowStorage(activity)) { - mSearchFeatureProvider.updateIndex(activity); + mSearchFeatureProvider.updateIndex(activity, this /* indexingCallback */); } else { Log.w(TAG, "Cannot update the Indexer as we are running low on storage space!"); } @@ -170,12 +180,7 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O @Override public void onResume() { super.onResume(); - if (TextUtils.isEmpty(mQuery)) { - return; - } - final String query = mQuery; - mQuery = ""; - onQueryTextChange(query); + requery(); } @Override @@ -218,6 +223,11 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O mNeverEnteredQuery = false; mQuery = query; + // If indexing is not finished, register the query text, but don't search. + if (!mSearchFeatureProvider.isIndexingComplete(getActivity())) { + return true; + } + if (isEmptyQuery) { final LoaderManager loaderManager = getLoaderManager(); loaderManager.destroyLoader(LOADER_ID_DATABASE); @@ -276,6 +286,22 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O public void onLoaderReset(Loader> loader) { } + /** + * Gets called when Indexing is completed. + */ + @Override + public void onIndexingFinished() { + if (mShowingSavedQuery) { + mSavedQueryController.loadSavedQueries(); + } else { + final LoaderManager loaderManager = getLoaderManager(); + loaderManager.initLoader(LOADER_ID_DATABASE, null, this); + loaderManager.initLoader(LOADER_ID_INSTALLED_APPS, null, this); + } + + requery(); + } + public void onSearchResultClicked() { mSavedQueryController.saveQuery(mQuery); mResultClickCount++; @@ -309,6 +335,15 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O return mSearchAdapter.getSearchResults(); } + private void requery() { + if (TextUtils.isEmpty(mQuery)) { + return; + } + final String query = mQuery; + mQuery = ""; + onQueryTextChange(query); + } + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) SearchView makeSearchView(ActionBar actionBar, String query) { final SearchView searchView = new SearchView(actionBar.getThemedContext()); diff --git a/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java index 63eeae071e2..e7331964a50 100644 --- a/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java @@ -18,7 +18,6 @@ package com.android.settings.search; import android.app.Activity; -import android.content.Context; import android.view.Menu; import com.android.settings.SettingsRobolectricTestRunner; @@ -35,14 +34,11 @@ import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.annotation.Config; + import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) @@ -73,19 +69,10 @@ public class SearchFeatureProviderImplTest { } @Test - public void getSiteMapManager_shouldCacheInstanec() { + public void getSiteMapManager_shouldCacheInstance() { final SiteMapManager manager1 = mProvider.getSiteMapManager(); final SiteMapManager manager2 = mProvider.getSiteMapManager(); assertThat(manager1).isSameAs(manager2); } - - @Test - public void testUpdateIndexNewSearch_UsesDatabaseIndexingManager() { - mProvider = spy(new SearchFeatureProviderImpl()); - when(mProvider.isEnabled(mActivity)).thenReturn(true); - - mProvider.updateIndex(mActivity); - verify(mProvider).getIndexingManager(any(Context.class)); - } } diff --git a/tests/robotests/src/com/android/settings/search2/DatabaseIndexingManagerTest.java b/tests/robotests/src/com/android/settings/search2/DatabaseIndexingManagerTest.java index 7bbda0fe2f0..0a8326dd6f1 100644 --- a/tests/robotests/src/com/android/settings/search2/DatabaseIndexingManagerTest.java +++ b/tests/robotests/src/com/android/settings/search2/DatabaseIndexingManagerTest.java @@ -37,6 +37,7 @@ import com.android.settings.R; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; import com.android.settings.search.IndexDatabaseHelper; +import com.android.settings.search.IndexingCallback; import com.android.settings.search.SearchIndexableRaw; import com.android.settings.testutils.DatabaseTestUtils; import com.android.settings.testutils.shadow.ShadowDatabaseIndexingUtils; @@ -47,6 +48,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowContentResolver; @@ -67,6 +69,7 @@ import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyList; import static org.mockito.Matchers.anyMap; import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -933,6 +936,32 @@ public class DatabaseIndexingManagerTest { assertThat(cursor.getCount()).isEqualTo(1); assertThat(cursor.getString(2)).isEqualTo(TITLE_ONE); } + + @Test + public void testUpdateAsyncTask_onPostExecute_performsCallback() { + IndexingCallback callback = mock(IndexingCallback.class); + + DatabaseIndexingManager.IndexingTask task = mManager.new IndexingTask(callback); + task.execute(); + + Robolectric.flushForegroundThreadScheduler(); + + verify(callback).onIndexingFinished(); + } + + @Test + public void testUpdateAsyncTask_onPostExecute_setsIndexingComplete() { + SearchFeatureProviderImpl provider = new SearchFeatureProviderImpl(); + DatabaseIndexingManager manager = spy(provider.getIndexingManager(mContext)); + DatabaseIndexingManager.IndexingTask task = manager.new IndexingTask(null); + doNothing().when(manager).performIndexing(); + + task.execute(); + Robolectric.flushForegroundThreadScheduler(); + + assertThat(provider.isIndexingComplete(mContext)).isTrue(); + } + // Util functions private SearchIndexableRaw getFakeRaw() { diff --git a/tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java b/tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java index 2b6ebafbb99..2296370c327 100644 --- a/tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java +++ b/tests/robotests/src/com/android/settings/search2/SearchFragmentTest.java @@ -26,6 +26,7 @@ import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; +import com.android.settings.search.IndexingCallback; import com.android.settings.testutils.FakeFeatureFactory; import org.junit.Before; @@ -42,6 +43,7 @@ import org.robolectric.util.ReflectionHelpers; import java.util.List; +import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; @@ -104,10 +106,7 @@ public class SearchFragmentTest { activityController = Robolectric.buildActivity(SearchActivity.class); activityController.setup(bundle); - verify(mFeatureFactory.searchFeatureProvider) - .getDatabaseSearchLoader(any(Context.class), anyString()); - verify(mFeatureFactory.searchFeatureProvider) - .getInstalledAppSearchLoader(any(Context.class), anyString()); + assertThat(fragment.mQuery).isEqualTo(testQuery); } @Test @@ -121,6 +120,8 @@ public class SearchFragmentTest { activityController.setup(); SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager() .findFragmentById(R.id.main_content); + when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) + .thenReturn(true); fragment.mQuery = ""; @@ -133,8 +134,6 @@ public class SearchFragmentTest { .getDatabaseSearchLoader(any(Context.class), anyString()); verify(mFeatureFactory.searchFeatureProvider, never()) .getInstalledAppSearchLoader(any(Context.class), anyString()); - verify(mFeatureFactory.searchFeatureProvider, times(2)) - .getSavedQueryLoader(any(Context.class)); } @Test @@ -154,6 +153,8 @@ public class SearchFragmentTest { activityController.setup(); SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager() .findFragmentById(R.id.main_content); + when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) + .thenReturn(true); fragment.onQueryTextChange(testQuery); activityController.get().onBackPressed(); @@ -181,15 +182,16 @@ public class SearchFragmentTest { .thenReturn(mInstalledAppResultLoader); when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class))) .thenReturn(mSavedQueryLoader); - ActivityController activityController = Robolectric.buildActivity(SearchActivity.class); activityController.setup(); - SearchFragment fragment = spy((SearchFragment) activityController.get().getFragmentManager() .findFragmentById(R.id.main_content)); + when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) + .thenReturn(true); ReflectionHelpers.setField(fragment, "mSavedQueryController", mSavedQueryController); fragment.mQuery = "123"; + fragment.onQueryTextChange(""); verify(mFeatureFactory.searchFeatureProvider, never()) @@ -215,9 +217,12 @@ public class SearchFragmentTest { activityController.setup(); SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager() .findFragmentById(R.id.main_content); + when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) + .thenReturn(true); fragment.onAttach(null); - verify(mFeatureFactory.searchFeatureProvider).updateIndex(any(Context.class)); + verify(mFeatureFactory.searchFeatureProvider).updateIndex(any(Context.class), + any(IndexingCallback.class)); } @Test @@ -237,6 +242,8 @@ public class SearchFragmentTest { SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager() .findFragmentById(R.id.main_content)); + when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) + .thenReturn(true); fragment.onQueryTextChange("non-empty"); @@ -261,11 +268,11 @@ public class SearchFragmentTest { activityController.setup(); SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager() .findFragmentById(R.id.main_content)); - + when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) + .thenReturn(true); when(fragment.getLoaderManager()).thenReturn(mock(LoaderManager.class)); fragment.onQueryTextChange(""); - Robolectric.flushForegroundThreadScheduler(); verify(mFeatureFactory.searchFeatureProvider).hideFeedbackButton(); @@ -281,18 +288,65 @@ public class SearchFragmentTest { .thenReturn(new MockAppLoader(RuntimeEnvironment.application)); when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class))) .thenReturn(mSavedQueryLoader); + ActivityController activityController = + Robolectric.buildActivity(SearchActivity.class); + activityController.setup(); + SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager() + .findFragmentById(R.id.main_content); + when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) + .thenReturn(true); + fragment.onQueryTextChange("non-empty"); + Robolectric.flushForegroundThreadScheduler(); + + verify(mFeatureFactory.searchFeatureProvider).showFeedbackButton(any(SearchFragment.class), + any(View.class)); + } + + @Test + public void preIndexingFinished_isIndexingFinishedFlag_isFalse() { ActivityController activityController = Robolectric.buildActivity(SearchActivity.class); activityController.setup(); SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager() .findFragmentById(R.id.main_content); - fragment.onQueryTextChange("non-empty"); + when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) + .thenReturn(false); + } - Robolectric.flushForegroundThreadScheduler(); + @Test + public void onIndexingFinished_notShowingSavedQuery_initLoaders() { + ActivityController activityController = + Robolectric.buildActivity(SearchActivity.class); + activityController.setup(); + SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager() + .findFragmentById(R.id.main_content)); + final LoaderManager loaderManager = mock(LoaderManager.class); + when(fragment.getLoaderManager()).thenReturn(loaderManager); + fragment.mShowingSavedQuery = false; + fragment.mQuery = null; - verify(mFeatureFactory.searchFeatureProvider).showFeedbackButton(any(SearchFragment.class), - any(View.class)); + fragment.onIndexingFinished(); + + verify(loaderManager).initLoader(eq(SearchFragment.LOADER_ID_DATABASE), + eq(null), any(LoaderManager.LoaderCallbacks.class)); + verify(loaderManager).initLoader(eq(SearchFragment.LOADER_ID_INSTALLED_APPS), + eq(null), any(LoaderManager.LoaderCallbacks.class)); + } + + @Test + public void onIndexingFinished_showingSavedQuery_loadsSavedQueries() { + ActivityController activityController = + Robolectric.buildActivity(SearchActivity.class); + activityController.setup(); + SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager() + .findFragmentById(R.id.main_content)); + fragment.mShowingSavedQuery = true; + ReflectionHelpers.setField(fragment, "mSavedQueryController", mSavedQueryController); + + fragment.onIndexingFinished(); + + verify(fragment.mSavedQueryController).loadSavedQueries(); } }