AllAppsSearchBarController.java revision a81f580fb71c3e867ccbdef1d034ab17835318fb
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.TextWatcher;
24import android.view.KeyEvent;
25import android.view.View;
26import android.view.inputmethod.EditorInfo;
27import android.view.inputmethod.InputMethodManager;
28import android.widget.TextView;
29import android.widget.TextView.OnEditorActionListener;
30
31import com.android.launcher3.ExtendedEditText;
32import com.android.launcher3.Launcher;
33import com.android.launcher3.Utilities;
34import com.android.launcher3.util.ComponentKey;
35
36import java.util.ArrayList;
37
38/**
39 * An interface to a search box that AllApps can command.
40 */
41public abstract class AllAppsSearchBarController
42        implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener {
43
44    protected Launcher mLauncher;
45    protected AlphabeticalAppsList mApps;
46    protected Callbacks mCb;
47    protected ExtendedEditText mInput;
48    private String mQuery;
49
50    protected DefaultAppSearchAlgorithm mSearchAlgorithm;
51    protected InputMethodManager mInputMethodManager;
52
53    public void setVisibility(int visibility) {
54        mInput.setVisibility(visibility);
55    }
56    /**
57     * Sets the references to the apps model and the search result callback.
58     */
59    public final void initialize(
60            AlphabeticalAppsList apps, ExtendedEditText input,
61            Launcher launcher, Callbacks cb) {
62        mApps = apps;
63        mCb = cb;
64        mLauncher = launcher;
65
66        mInput = input;
67        mInput.addTextChangedListener(this);
68        mInput.setOnEditorActionListener(this);
69        mInput.setOnBackKeyListener(this);
70
71        mInputMethodManager = (InputMethodManager)
72                mInput.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
73
74        mSearchAlgorithm = onInitializeSearch();
75    }
76
77    /**
78     * To be implemented by subclasses. This method will get called when the controller is set.
79     */
80    protected abstract DefaultAppSearchAlgorithm onInitializeSearch();
81
82    @Override
83    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
84        // Do nothing
85    }
86
87    @Override
88    public void onTextChanged(CharSequence s, int start, int before, int count) {
89        // Do nothing
90    }
91
92    @Override
93    public void afterTextChanged(final Editable s) {
94        mQuery = s.toString();
95        if (mQuery.isEmpty()) {
96            mSearchAlgorithm.cancel(true);
97            mCb.clearSearchResult();
98        } else {
99            mSearchAlgorithm.cancel(false);
100            mSearchAlgorithm.doSearch(mQuery, mCb);
101        }
102    }
103
104    protected void refreshSearchResult() {
105        if (mQuery == null) {
106            return;
107        }
108        // If play store continues auto updating an app, we want to show partial result.
109        mSearchAlgorithm.cancel(false);
110        mSearchAlgorithm.doSearch(mQuery, mCb);
111    }
112
113    @Override
114    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
115        // Skip if it's not the right action
116        if (actionId != EditorInfo.IME_ACTION_SEARCH) {
117            return false;
118        }
119        // Skip if the query is empty
120        String query = v.getText().toString();
121        if (query.isEmpty()) {
122            return false;
123        }
124        return mLauncher.startActivitySafely(v, createMarketSearchIntent(query), null);
125    }
126
127    @Override
128    public boolean onBackKey() {
129        // Only hide the search field if there is no query, or if there
130        // are no filtered results
131        String query = Utilities.trim(mInput.getEditableText().toString());
132        if (query.isEmpty() || mApps.hasNoFilteredResults()) {
133            reset();
134            return true;
135        }
136        return false;
137    }
138
139    /**
140     * Resets the search bar state.
141     */
142    public void reset() {
143        unfocusSearchField();
144        mCb.clearSearchResult();
145        mInput.setText("");
146        // We need to reset this after we clear the input text
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}