AllAppsSearchBarController.java revision 45a11dd9ee5c9cdaa65527ae0ef1c8b004417f28
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.launcher3.allapps;
17
18import android.content.Context;
19import android.content.Intent;
20import android.graphics.Rect;
21import android.net.Uri;
22import android.text.Editable;
23import android.text.TextUtils;
24import android.text.TextWatcher;
25import android.view.KeyEvent;
26import android.view.View;
27import android.view.inputmethod.EditorInfo;
28import android.view.inputmethod.InputMethodManager;
29import android.widget.TextView;
30import android.widget.TextView.OnEditorActionListener;
31
32import com.android.launcher3.ExtendedEditText;
33import com.android.launcher3.Launcher;
34import com.android.launcher3.Utilities;
35import com.android.launcher3.util.ComponentKey;
36
37import java.util.ArrayList;
38
39/**
40 * An interface to a search box that AllApps can command.
41 */
42public abstract class AllAppsSearchBarController
43        implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener {
44
45    protected Launcher mLauncher;
46    protected AlphabeticalAppsList mApps;
47    protected Callbacks mCb;
48    protected ExtendedEditText mInput;
49    private String mQuery;
50
51    protected DefaultAppSearchAlgorithm mSearchAlgorithm;
52    protected InputMethodManager mInputMethodManager;
53
54    public void setVisibility(int visibility) {
55        mInput.setVisibility(visibility);
56    }
57    /**
58     * Sets the references to the apps model and the search result callback.
59     */
60    public final void initialize(
61            AlphabeticalAppsList apps, ExtendedEditText input,
62            Launcher launcher, Callbacks cb) {
63        mApps = apps;
64        mCb = cb;
65        mLauncher = launcher;
66
67        mInput = input;
68        mInput.addTextChangedListener(this);
69        mInput.setOnEditorActionListener(this);
70        mInput.setOnBackKeyListener(this);
71
72        mInputMethodManager = (InputMethodManager)
73                mInput.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
74
75        mSearchAlgorithm = onInitializeSearch();
76    }
77
78    /**
79     * To be implemented by subclasses. This method will get called when the controller is set.
80     */
81    protected abstract DefaultAppSearchAlgorithm onInitializeSearch();
82
83    @Override
84    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
85        // Do nothing
86    }
87
88    @Override
89    public void onTextChanged(CharSequence s, int start, int before, int count) {
90        // Do nothing
91    }
92
93    @Override
94    public void afterTextChanged(final Editable s) {
95        mQuery = s.toString();
96        if (mQuery.isEmpty()) {
97            mSearchAlgorithm.cancel(true);
98            mCb.clearSearchResult();
99        } else {
100            mSearchAlgorithm.cancel(false);
101            mSearchAlgorithm.doSearch(mQuery, mCb);
102        }
103    }
104
105    protected void refreshSearchResult() {
106        if (TextUtils.isEmpty(mQuery)) {
107            return;
108        }
109        // If play store continues auto updating an app, we want to show partial result.
110        mSearchAlgorithm.cancel(false);
111        mSearchAlgorithm.doSearch(mQuery, mCb);
112    }
113
114    @Override
115    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
116        // Skip if it's not the right action
117        if (actionId != EditorInfo.IME_ACTION_SEARCH) {
118            return false;
119        }
120        // Skip if the query is empty
121        String query = v.getText().toString();
122        if (query.isEmpty()) {
123            return false;
124        }
125        return mLauncher.startActivitySafely(v, createMarketSearchIntent(query), null);
126    }
127
128    @Override
129    public boolean onBackKey() {
130        // Only hide the search field if there is no query, or if there
131        // are no filtered results
132        String query = Utilities.trim(mInput.getEditableText().toString());
133        if (query.isEmpty() || mApps.hasNoFilteredResults()) {
134            reset();
135            return true;
136        }
137        return false;
138    }
139
140    /**
141     * Resets the search bar state.
142     */
143    public void reset() {
144        unfocusSearchField();
145        mCb.clearSearchResult();
146        mInput.setText("");
147        mQuery = null;
148        hideKeyboard();
149    }
150
151    protected void hideKeyboard() {
152        mInputMethodManager.hideSoftInputFromWindow(mInput.getWindowToken(), 0);
153    }
154
155    protected void unfocusSearchField() {
156        View nextFocus = mInput.focusSearch(View.FOCUS_DOWN);
157        if (nextFocus != null) {
158            nextFocus.requestFocus();
159        }
160    }
161
162    /**
163     * Focuses the search field to handle key events.
164     */
165    public void focusSearchField() {
166        mInput.requestFocus();
167        mInputMethodManager.showSoftInput(mInput, InputMethodManager.SHOW_IMPLICIT);
168    }
169
170    /**
171     * Returns whether the search field is focused.
172     */
173    public boolean isSearchFieldFocused() {
174        return mInput.isFocused();
175    }
176
177    /**
178     * Creates a new market search intent.
179     */
180    public Intent createMarketSearchIntent(String query) {
181        Uri marketSearchUri = Uri.parse("market://search")
182                .buildUpon()
183                .appendQueryParameter("c", "apps")
184                .appendQueryParameter("q", query)
185                .build();
186        return new Intent(Intent.ACTION_VIEW).setData(marketSearchUri);
187    }
188
189    /**
190     * Callback for getting search results.
191     */
192    public interface Callbacks {
193
194        /**
195         * Called when the bounds of the search bar has changed.
196         */
197        @Deprecated
198        void onBoundsChanged(Rect newBounds);
199
200        /**
201         * Called when the search is complete.
202         *
203         * @param apps sorted list of matching components or null if in case of failure.
204         */
205        void onSearchResult(String query, ArrayList<ComponentKey> apps);
206
207        /**
208         * Called when the search results should be cleared.
209         */
210        void clearSearchResult();
211    }
212}