Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0104852d authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Refactor all saved query related logic into a controller"

parents 8d26d761 8c3dc96b
Loading
Loading
Loading
Loading
+96 −0
Original line number Diff line number Diff line
/*
 * 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.app.LoaderManager;
import android.content.Context;
import android.content.Loader;
import android.os.Bundle;

import com.android.settings.overlay.FeatureFactory;

import java.util.List;

public class SavedQueryController implements LoaderManager.LoaderCallbacks {

    // TODO: make a generic background task manager to handle one-off tasks like this one.

    private static final int LOADER_ID_SAVE_QUERY_TASK = 0;
    private static final int LOADER_ID_REMOVE_QUERY_TASK = 1;
    private static final int LOADER_ID_SAVED_QUERIES = 2;
    private static final String ARG_QUERY = "remove_query";

    private final Context mContext;
    private final LoaderManager mLoaderManager;
    private final SearchFeatureProvider mSearchFeatureProvider;
    private final SearchResultsAdapter mResultAdapter;

    public SavedQueryController(Context context, LoaderManager loaderManager,
            SearchResultsAdapter resultsAdapter) {
        mContext = context;
        mLoaderManager = loaderManager;
        mResultAdapter = resultsAdapter;
        mSearchFeatureProvider = FeatureFactory.getFactory(context)
                .getSearchFeatureProvider();
    }

    @Override
    public Loader onCreateLoader(int id, Bundle args) {
        switch (id) {
            case LOADER_ID_SAVE_QUERY_TASK:
                return new SavedQueryRecorder(mContext, args.getString(ARG_QUERY));
            case LOADER_ID_REMOVE_QUERY_TASK:
                return new SavedQueryRemover(mContext, args.getString(ARG_QUERY));
            case LOADER_ID_SAVED_QUERIES:
                return mSearchFeatureProvider.getSavedQueryLoader(mContext);
        }
        return null;
    }

    @Override
    public void onLoadFinished(Loader loader, Object data) {
        switch (loader.getId()) {
            case LOADER_ID_REMOVE_QUERY_TASK:
                mLoaderManager.restartLoader(LOADER_ID_SAVED_QUERIES, null, this);
                break;
            case LOADER_ID_SAVED_QUERIES:
                mResultAdapter.displaySavedQuery((List<SearchResult>) data);
                break;
        }
    }

    @Override
    public void onLoaderReset(Loader loader) {

    }

    public void saveQuery(String query) {
        final Bundle args = new Bundle();
        args.putString(ARG_QUERY, query);
        mLoaderManager.restartLoader(LOADER_ID_SAVE_QUERY_TASK, args, this);
    }

    public void removeQuery(String query) {
        final Bundle args = new Bundle();
        args.putString(ARG_QUERY, query);
        mLoaderManager.restartLoader(LOADER_ID_REMOVE_QUERY_TASK, args, this);
    }

    public void loadSavedQueries() {
        mLoaderManager.restartLoader(LOADER_ID_SAVED_QUERIES, null, this);
    }
}
+26 −65
Original line number Diff line number Diff line
@@ -54,11 +54,11 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O

    // State values
    private static final String STATE_QUERY = "state_query";
    private static final String STATE_SHOWING_SAVED_QUERY = "state_showing_saved_query";
    private static final String STATE_NEVER_ENTERED_QUERY = "state_never_entered_query";
    private static final String STATE_RESULT_CLICK_COUNT = "state_result_click_count";

    // Loader IDs
    private static final int LOADER_ID_RECENTS = 0;
    private static final int LOADER_ID_DATABASE = 1;
    private static final int LOADER_ID_INSTALLED_APPS = 2;

@@ -74,12 +74,11 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
    @VisibleForTesting
    String mQuery;

    private final SaveQueryCallback mSaveQueryCallback =
            new SaveQueryCallback();

    private boolean mNeverEnteredQuery = true;
    private boolean mShowingSavedQuery;
    private int mResultClickCount;
    private MetricsFeatureProvider mMetricsFeatureProvider;
    private SavedQueryController mSavedQueryController;

    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    SearchFeatureProvider mSearchFeatureProvider;
@@ -117,20 +116,26 @@ 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(
                getContext(), loaderManager, mSearchAdapter);
        mSearchFeatureProvider.initFeedbackButton();

        final LoaderManager loaderManager = getLoaderManager();

        if (savedInstanceState != null) {
            mQuery = savedInstanceState.getString(STATE_QUERY);
            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 {
            loaderManager.initLoader(LOADER_ID_RECENTS, null, this);
            mShowingSavedQuery = true;
            mSavedQueryController.loadSavedQueries();
        }

        final Activity activity = getActivity();
@@ -180,6 +185,7 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
        super.onSaveInstanceState(outState);
        outState.putString(STATE_QUERY, mQuery);
        outState.putBoolean(STATE_NEVER_ENTERED_QUERY, mNeverEnteredQuery);
        outState.putBoolean(STATE_SHOWING_SAVED_QUERY, mShowingSavedQuery);
        outState.putInt(STATE_RESULT_CLICK_COUNT, mResultClickCount);
    }

@@ -206,7 +212,8 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
            final LoaderManager loaderManager = getLoaderManager();
            loaderManager.destroyLoader(LOADER_ID_DATABASE);
            loaderManager.destroyLoader(LOADER_ID_INSTALLED_APPS);
            loaderManager.restartLoader(LOADER_ID_RECENTS, null /* args */, this /* callback */);
            mShowingSavedQuery = true;
            mSavedQueryController.loadSavedQueries();
            mSearchFeatureProvider.hideFeedbackButton();
        } else {
            restartLoaders();
@@ -218,8 +225,7 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
    @Override
    public boolean onQueryTextSubmit(String query) {
        // Save submitted query.
        getLoaderManager().restartLoader(SaveQueryCallback.LOADER_ID_SAVE_QUERY_TASK, null,
                mSaveQueryCallback);
        mSavedQueryController.saveQuery(mQuery);
        hideKeyboard();
        return true;
    }
@@ -233,8 +239,6 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
                return mSearchFeatureProvider.getDatabaseSearchLoader(activity, mQuery);
            case LOADER_ID_INSTALLED_APPS:
                return mSearchFeatureProvider.getInstalledAppSearchLoader(activity, mQuery);
            case LOADER_ID_RECENTS:
                return mSearchFeatureProvider.getSavedQueryLoader(activity);
            default:
                return null;
        }
@@ -243,18 +247,11 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
    @Override
    public void onLoadFinished(Loader<List<? extends SearchResult>> loader,
            List<? extends SearchResult> data) {
        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();
        }
        final int resultCount = mSearchAdapter.displaySearchResults();
        mNoResultsView.setVisibility(resultCount == 0 ? View.VISIBLE : View.GONE);
        mSearchFeatureProvider.showFeedbackButton(this, getView());
    }
@@ -264,8 +261,7 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
    }

    public void onSearchResultClicked() {
        getLoaderManager().restartLoader(SaveQueryCallback.LOADER_ID_SAVE_QUERY_TASK, null,
                mSaveQueryCallback);
        mSavedQueryController.saveQuery(mQuery);
        mResultClickCount++;
    }

@@ -278,13 +274,11 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
    }

    public void onRemoveSavedQueryClicked(CharSequence title) {
        final Bundle args = new Bundle();
        args.putString(SaveQueryCallback.ARG_REMOVE_QUERY, title.toString());
        getLoaderManager().restartLoader(SaveQueryCallback.LOADER_ID_REMOVE_QUERY_TASK,
                args, mSaveQueryCallback);
        mSavedQueryController.removeQuery(title.toString());
    }

    private void restartLoaders() {
        mShowingSavedQuery = false;
        final LoaderManager loaderManager = getLoaderManager();
        mUnfinishedLoadersCount.set(NUM_QUERY_LOADERS);
        loaderManager.restartLoader(LOADER_ID_DATABASE, null /* args */, this /* callback */);
@@ -325,37 +319,4 @@ public class SearchFragment extends InstrumentedFragment implements SearchView.O
            mResultsRecyclerView.requestFocus();
        }
    }

    private class SaveQueryCallback implements LoaderManager.LoaderCallbacks<Void> {
        // TODO: make a generic background task manager to handle one-off tasks like this one.

        private static final int LOADER_ID_SAVE_QUERY_TASK = 0;
        private static final int LOADER_ID_REMOVE_QUERY_TASK = 1;
        private static final String ARG_REMOVE_QUERY = "remove_query";

        @Override
        public Loader<Void> onCreateLoader(int id, Bundle args) {
            switch (id) {
                case LOADER_ID_SAVE_QUERY_TASK:
                    return new SavedQueryRecorder(getActivity(), mQuery);
                case LOADER_ID_REMOVE_QUERY_TASK:
                    return new SavedQueryRemover(getActivity(), args.getString(ARG_REMOVE_QUERY));
            }
            return null;
        }

        @Override
        public void onLoadFinished(Loader<Void> loader, Void data) {
            switch (loader.getId()) {
                case LOADER_ID_REMOVE_QUERY_TASK:
                    getLoaderManager().restartLoader(LOADER_ID_RECENTS, null, SearchFragment.this);
                    break;
            }
        }

        @Override
        public void onLoaderReset(Loader<Void> loader) {

        }
    }
}
 No newline at end of file
+21 −22
Original line number Diff line number Diff line
@@ -43,7 +43,6 @@ 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;
@@ -66,7 +65,8 @@ public class SearchFragmentTest {

    @Mock
    private SavedQueryLoader mSavedQueryLoader;

    @Mock
    private SavedQueryController mSavedQueryController;
    private FakeFeatureFactory mFeatureFactory;

    @Before
@@ -96,6 +96,7 @@ public class SearchFragmentTest {
        SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager()
                .findFragmentById(R.id.main_content);

        ReflectionHelpers.setField(fragment, "mShowingSavedQuery", false);
        fragment.mQuery = testQuery;

        activityController.saveInstanceState(bundle).pause().stop().destroy();
@@ -111,12 +112,6 @@ public class SearchFragmentTest {

    @Test
    public void screenRotateEmptyString_ShouldNotCrash() {
        when(mFeatureFactory.searchFeatureProvider
                .getDatabaseSearchLoader(any(Context.class), anyString()))
                .thenReturn(mDatabaseResultLoader);
        when(mFeatureFactory.searchFeatureProvider
                .getInstalledAppSearchLoader(any(Context.class), anyString()))
                .thenReturn(mInstalledAppResultLoader);
        when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
                .thenReturn(mSavedQueryLoader);

@@ -134,10 +129,12 @@ public class SearchFragmentTest {
        activityController = Robolectric.buildActivity(SearchActivity.class);
        activityController.setup(bundle);

        verify(mFeatureFactory.searchFeatureProvider)
        verify(mFeatureFactory.searchFeatureProvider, never())
                .getDatabaseSearchLoader(any(Context.class), anyString());
        verify(mFeatureFactory.searchFeatureProvider)
        verify(mFeatureFactory.searchFeatureProvider, never())
                .getInstalledAppSearchLoader(any(Context.class), anyString());
        verify(mFeatureFactory.searchFeatureProvider, times(2))
                .getSavedQueryLoader(any(Context.class));
    }

    @Test
@@ -160,6 +157,7 @@ public class SearchFragmentTest {

        fragment.onQueryTextChange(testQuery);
        activityController.get().onBackPressed();

        activityController.pause().stop().destroy();

        verify(mFeatureFactory.metricsFeatureProvider, never()).action(
@@ -174,7 +172,7 @@ public class SearchFragmentTest {
    }

    @Test
    public void queryTextChangeToEmpty_shouldTriggerSavedQueryLoader() {
    public void queryTextChangeToEmpty_shouldLoadSavedQuery() {
        when(mFeatureFactory.searchFeatureProvider
                .getDatabaseSearchLoader(any(Context.class), anyString()))
                .thenReturn(mDatabaseResultLoader);
@@ -190,20 +188,15 @@ public class SearchFragmentTest {

        SearchFragment fragment = spy((SearchFragment) activityController.get().getFragmentManager()
                .findFragmentById(R.id.main_content));

        final SearchResultsAdapter adapter = mock(SearchResultsAdapter.class);
        ReflectionHelpers.setField(fragment, "mSearchAdapter", adapter);
        ReflectionHelpers.setField(fragment, "mSavedQueryController", mSavedQueryController);
        fragment.mQuery = "123";
        fragment.onQueryTextChange("");

        verify(mFeatureFactory.searchFeatureProvider, never())
                .getDatabaseSearchLoader(any(Context.class), anyString());
        verify(mFeatureFactory.searchFeatureProvider, never())
                .getInstalledAppSearchLoader(any(Context.class), anyString());
        verify(mFeatureFactory.searchFeatureProvider)
                .getSavedQueryLoader(any(Context.class));

        fragment.onLoadFinished(mSavedQueryLoader, null /* data */);

        verify(adapter).displaySavedQuery(anyList());
        verify(mSavedQueryController).loadSavedQueries();
    }

    @Test
@@ -235,6 +228,8 @@ public class SearchFragmentTest {
        when(mFeatureFactory.searchFeatureProvider
                .getInstalledAppSearchLoader(any(Context.class), anyString()))
                .thenReturn(new MockAppLoader(RuntimeEnvironment.application));
        when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
                .thenReturn(mSavedQueryLoader);

        ActivityController<SearchActivity> activityController =
                Robolectric.buildActivity(SearchActivity.class);
@@ -258,6 +253,8 @@ public class SearchFragmentTest {
        when(mFeatureFactory.searchFeatureProvider
                .getInstalledAppSearchLoader(any(Context.class), anyString()))
                .thenReturn(new MockAppLoader(RuntimeEnvironment.application));
        when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
                .thenReturn(mSavedQueryLoader);

        ActivityController<SearchActivity> activityController =
                Robolectric.buildActivity(SearchActivity.class);
@@ -282,12 +279,14 @@ public class SearchFragmentTest {
        when(mFeatureFactory.searchFeatureProvider
                .getInstalledAppSearchLoader(any(Context.class), anyString()))
                .thenReturn(new MockAppLoader(RuntimeEnvironment.application));
        when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class)))
                .thenReturn(mSavedQueryLoader);

        ActivityController<SearchActivity> activityController =
                Robolectric.buildActivity(SearchActivity.class);
        activityController.setup();
        SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager()
                .findFragmentById(R.id.main_content));
        SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager()
                .findFragmentById(R.id.main_content);

        fragment.onQueryTextChange("non-empty");