1ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung/*
2ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung * Copyright (C) 2015 The Android Open Source Project
3ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung *
4ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung * Licensed under the Apache License, Version 2.0 (the "License");
5ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung * you may not use this file except in compliance with the License.
6ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung * You may obtain a copy of the License at
7ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung *
8ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung *      http://www.apache.org/licenses/LICENSE-2.0
9ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung *
10ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung * Unless required by applicable law or agreed to in writing, software
11ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung * distributed under the License is distributed on an "AS IS" BASIS,
12ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung * See the License for the specific language governing permissions and
14ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung * limitations under the License.
15ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung */
16ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chungpackage com.android.launcher3.allapps;
17ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung
180ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyalimport android.content.Context;
192494c3f1681e71af06556ef47de16f018811f7e3Sunny Goyalimport android.content.Intent;
20ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chungimport android.graphics.Rect;
212494c3f1681e71af06556ef47de16f018811f7e3Sunny Goyalimport android.net.Uri;
220ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyalimport android.text.Editable;
2345a11dd9ee5c9cdaa65527ae0ef1c8b004417f28Winsonimport android.text.TextUtils;
240ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyalimport android.text.TextWatcher;
250ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyalimport android.view.KeyEvent;
26ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chungimport android.view.View;
270ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyalimport android.view.inputmethod.EditorInfo;
280ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyalimport android.view.inputmethod.InputMethodManager;
290ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyalimport android.widget.TextView;
300ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyalimport android.widget.TextView.OnEditorActionListener;
31ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung
320ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyalimport com.android.launcher3.ExtendedEditText;
339e3fee1427c0baa38564e20a9f351d1a87c25761Sunny Goyalimport com.android.launcher3.Launcher;
340ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyalimport com.android.launcher3.Utilities;
355183285847816cee9d0db6a8a7ab1a5929163e4eSunny Goyalimport com.android.launcher3.util.ComponentKey;
365183285847816cee9d0db6a8a7ab1a5929163e4eSunny Goyal
37ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chungimport java.util.ArrayList;
38ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung
39ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung/**
40ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung * An interface to a search box that AllApps can command.
41ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung */
420ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyalpublic abstract class AllAppsSearchBarController
43ce3fffb5fb446e031e52ee0c5f2a4a5cf86a8c81Sunny Goyal        implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener {
44ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung
459e3fee1427c0baa38564e20a9f351d1a87c25761Sunny Goyal    protected Launcher mLauncher;
46ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung    protected AlphabeticalAppsList mApps;
47ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung    protected Callbacks mCb;
480ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    protected ExtendedEditText mInput;
49f66b6802392bc473d7e1f86a58cf65b25ed2148cHyunyoung Song    private String mQuery;
500ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal
510ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    protected DefaultAppSearchAlgorithm mSearchAlgorithm;
520ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    protected InputMethodManager mInputMethodManager;
53ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung
54645764e3e5fa34d9adcddfc722d726b76f048306Hyunyoung Song    public void setVisibility(int visibility) {
55645764e3e5fa34d9adcddfc722d726b76f048306Hyunyoung Song        mInput.setVisibility(visibility);
56645764e3e5fa34d9adcddfc722d726b76f048306Hyunyoung Song    }
57ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung    /**
58ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung     * Sets the references to the apps model and the search result callback.
59ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung     */
600ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    public final void initialize(
610ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal            AlphabeticalAppsList apps, ExtendedEditText input,
629e3fee1427c0baa38564e20a9f351d1a87c25761Sunny Goyal            Launcher launcher, Callbacks cb) {
63ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung        mApps = apps;
64ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung        mCb = cb;
659e3fee1427c0baa38564e20a9f351d1a87c25761Sunny Goyal        mLauncher = launcher;
660ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal
670ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        mInput = input;
680ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        mInput.addTextChangedListener(this);
690ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        mInput.setOnEditorActionListener(this);
700ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        mInput.setOnBackKeyListener(this);
710ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal
720ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        mInputMethodManager = (InputMethodManager)
730ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal                mInput.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
740ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal
750ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        mSearchAlgorithm = onInitializeSearch();
76ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung    }
77ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung
784919827990b16ae22595d0b7cb123a875961d9beSunny Goyal    /**
792494c3f1681e71af06556ef47de16f018811f7e3Sunny Goyal     * To be implemented by subclasses. This method will get called when the controller is set.
804919827990b16ae22595d0b7cb123a875961d9beSunny Goyal     */
812494c3f1681e71af06556ef47de16f018811f7e3Sunny Goyal    protected abstract DefaultAppSearchAlgorithm onInitializeSearch();
820ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal
830ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    @Override
840ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
850ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        // Do nothing
860ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    }
870ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal
880ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    @Override
890ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    public void onTextChanged(CharSequence s, int start, int before, int count) {
900ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        // Do nothing
910ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    }
920ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal
930ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    @Override
940ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    public void afterTextChanged(final Editable s) {
95f66b6802392bc473d7e1f86a58cf65b25ed2148cHyunyoung Song        mQuery = s.toString();
96f66b6802392bc473d7e1f86a58cf65b25ed2148cHyunyoung Song        if (mQuery.isEmpty()) {
970ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal            mSearchAlgorithm.cancel(true);
980ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal            mCb.clearSearchResult();
990ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        } else {
1000ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal            mSearchAlgorithm.cancel(false);
101f66b6802392bc473d7e1f86a58cf65b25ed2148cHyunyoung Song            mSearchAlgorithm.doSearch(mQuery, mCb);
102f66b6802392bc473d7e1f86a58cf65b25ed2148cHyunyoung Song        }
103f66b6802392bc473d7e1f86a58cf65b25ed2148cHyunyoung Song    }
104f66b6802392bc473d7e1f86a58cf65b25ed2148cHyunyoung Song
105f66b6802392bc473d7e1f86a58cf65b25ed2148cHyunyoung Song    protected void refreshSearchResult() {
10645a11dd9ee5c9cdaa65527ae0ef1c8b004417f28Winson        if (TextUtils.isEmpty(mQuery)) {
107f66b6802392bc473d7e1f86a58cf65b25ed2148cHyunyoung Song            return;
1080ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        }
109f66b6802392bc473d7e1f86a58cf65b25ed2148cHyunyoung Song        // If play store continues auto updating an app, we want to show partial result.
110f66b6802392bc473d7e1f86a58cf65b25ed2148cHyunyoung Song        mSearchAlgorithm.cancel(false);
111f66b6802392bc473d7e1f86a58cf65b25ed2148cHyunyoung Song        mSearchAlgorithm.doSearch(mQuery, mCb);
1120ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    }
1130ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal
1140ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    @Override
1150ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
1160ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        // Skip if it's not the right action
1170ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        if (actionId != EditorInfo.IME_ACTION_SEARCH) {
1180ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal            return false;
1190ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        }
1209e3fee1427c0baa38564e20a9f351d1a87c25761Sunny Goyal        // Skip if the query is empty
1219e3fee1427c0baa38564e20a9f351d1a87c25761Sunny Goyal        String query = v.getText().toString();
1229e3fee1427c0baa38564e20a9f351d1a87c25761Sunny Goyal        if (query.isEmpty()) {
1230ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal            return false;
1240ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        }
1252494c3f1681e71af06556ef47de16f018811f7e3Sunny Goyal        return mLauncher.startActivitySafely(v, createMarketSearchIntent(query), null);
1260ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    }
1270ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal
1280ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    @Override
1290ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    public boolean onBackKey() {
130bb918b3532f0f88177a9f38f55440121305c79ceJon Miranda        // Only hide the search field if there is no query
1310ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        String query = Utilities.trim(mInput.getEditableText().toString());
132bb918b3532f0f88177a9f38f55440121305c79ceJon Miranda        if (query.isEmpty()) {
1330ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal            reset();
1340ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal            return true;
1350ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        }
1360ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        return false;
1370ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    }
1380ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal
1390ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    /**
1400ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal     * Resets the search bar state.
1410ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal     */
1420ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    public void reset() {
1430ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        unfocusSearchField();
1440ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        mCb.clearSearchResult();
1450ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        mInput.setText("");
146a81f580fb71c3e867ccbdef1d034ab17835318fbWinson        mQuery = null;
14735d96306ad6d8c404cfaa90e1d68fb194a27a2faPeter Schiller        hideKeyboard();
14835d96306ad6d8c404cfaa90e1d68fb194a27a2faPeter Schiller    }
14935d96306ad6d8c404cfaa90e1d68fb194a27a2faPeter Schiller
15035d96306ad6d8c404cfaa90e1d68fb194a27a2faPeter Schiller    protected void hideKeyboard() {
1510ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        mInputMethodManager.hideSoftInputFromWindow(mInput.getWindowToken(), 0);
1520ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    }
1530ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal
1540ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    protected void unfocusSearchField() {
1550ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        View nextFocus = mInput.focusSearch(View.FOCUS_DOWN);
1560ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        if (nextFocus != null) {
1570ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal            nextFocus.requestFocus();
1580ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        }
1590ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    }
160ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung
161ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung    /**
162ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung     * Focuses the search field to handle key events.
163ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung     */
1640ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    public void focusSearchField() {
165c2fe1147f9802d581d1e0c1da4dcaaf8ebdfa939Hyunyoung Song        mInput.showKeyboard();
1660ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    }
167ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung
168ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung    /**
169ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung     * Returns whether the search field is focused.
170ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung     */
1710ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    public boolean isSearchFieldFocused() {
1720ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal        return mInput.isFocused();
1730ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    }
174ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung
175ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung    /**
1762494c3f1681e71af06556ef47de16f018811f7e3Sunny Goyal     * Creates a new market search intent.
177ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung     */
1782494c3f1681e71af06556ef47de16f018811f7e3Sunny Goyal    public Intent createMarketSearchIntent(String query) {
1792494c3f1681e71af06556ef47de16f018811f7e3Sunny Goyal        Uri marketSearchUri = Uri.parse("market://search")
1802494c3f1681e71af06556ef47de16f018811f7e3Sunny Goyal                .buildUpon()
1812494c3f1681e71af06556ef47de16f018811f7e3Sunny Goyal                .appendQueryParameter("c", "apps")
1822494c3f1681e71af06556ef47de16f018811f7e3Sunny Goyal                .appendQueryParameter("q", query)
1832494c3f1681e71af06556ef47de16f018811f7e3Sunny Goyal                .build();
1842494c3f1681e71af06556ef47de16f018811f7e3Sunny Goyal        return new Intent(Intent.ACTION_VIEW).setData(marketSearchUri);
1850ac7ede56afebe4401c0636196f5844be573ad68Sunny Goyal    }
186ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung
187ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung    /**
188ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung     * Callback for getting search results.
189ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung     */
190ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung    public interface Callbacks {
191ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung
192ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung        /**
193ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung         * Called when the bounds of the search bar has changed.
194ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung         */
19505c8c57fa72a81f34058036f6dc30c084ca6742bSunny Goyal        @Deprecated
196ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung        void onBoundsChanged(Rect newBounds);
197ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung
198ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung        /**
199ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung         * Called when the search is complete.
200ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung         *
201ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung         * @param apps sorted list of matching components or null if in case of failure.
202ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung         */
2035183285847816cee9d0db6a8a7ab1a5929163e4eSunny Goyal        void onSearchResult(String query, ArrayList<ComponentKey> apps);
204ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung
205ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung        /**
206ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung         * Called when the search results should be cleared.
207ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung         */
208ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung        void clearSearchResult();
209ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung    }
210ef7f874a889b609bd34e692b9c9a1f8cefd1ea95Winson Chung}