AllAppsSearchBarController.java revision 2494c3f1681e71af06556ef47de16f018811f7e3
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
49    protected DefaultAppSearchAlgorithm mSearchAlgorithm;
50    protected InputMethodManager mInputMethodManager;
51
52    /**
53     * Sets the references to the apps model and the search result callback.
54     */
55    public final void initialize(
56            AlphabeticalAppsList apps, ExtendedEditText input,
57            Launcher launcher, Callbacks cb) {
58        mApps = apps;
59        mCb = cb;
60        mLauncher = launcher;
61
62        mInput = input;
63        mInput.addTextChangedListener(this);
64        mInput.setOnEditorActionListener(this);
65        mInput.setOnBackKeyListener(this);
66
67        mInputMethodManager = (InputMethodManager)
68                mInput.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
69
70        mSearchAlgorithm = onInitializeSearch();
71    }
72
73    /**
74     * To be implemented by subclasses. This method will get called when the controller is set.
75     */
76    protected abstract DefaultAppSearchAlgorithm onInitializeSearch();
77
78    @Override
79    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
80        // Do nothing
81    }
82
83    @Override
84    public void onTextChanged(CharSequence s, int start, int before, int count) {
85        // Do nothing
86    }
87
88    @Override
89    public void afterTextChanged(final Editable s) {
90        String query = s.toString();
91        if (query.isEmpty()) {
92            mSearchAlgorithm.cancel(true);
93            mCb.clearSearchResult();
94        } else {
95            mSearchAlgorithm.cancel(false);
96            mSearchAlgorithm.doSearch(query, mCb);
97        }
98    }
99
100    @Override
101    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
102        // Skip if it's not the right action
103        if (actionId != EditorInfo.IME_ACTION_SEARCH) {
104            return false;
105        }
106        // Skip if the query is empty
107        String query = v.getText().toString();
108        if (query.isEmpty()) {
109            return false;
110        }
111        return mLauncher.startActivitySafely(v, createMarketSearchIntent(query), null);
112    }
113
114    @Override
115    public boolean onBackKey() {
116        // Only hide the search field if there is no query, or if there
117        // are no filtered results
118        String query = Utilities.trim(mInput.getEditableText().toString());
119        if (query.isEmpty() || mApps.hasNoFilteredResults()) {
120            reset();
121            return true;
122        }
123        return false;
124    }
125
126    /**
127     * Resets the search bar state.
128     */
129    public void reset() {
130        unfocusSearchField();
131        mCb.clearSearchResult();
132        mInput.setText("");
133        mInputMethodManager.hideSoftInputFromWindow(mInput.getWindowToken(), 0);
134    }
135
136    protected void unfocusSearchField() {
137        View nextFocus = mInput.focusSearch(View.FOCUS_DOWN);
138        if (nextFocus != null) {
139            nextFocus.requestFocus();
140        }
141    }
142
143    /**
144     * Focuses the search field to handle key events.
145     */
146    public void focusSearchField() {
147        mInput.requestFocus();
148        mInputMethodManager.showSoftInput(mInput, InputMethodManager.SHOW_IMPLICIT);
149    }
150
151    /**
152     * Returns whether the search field is focused.
153     */
154    public boolean isSearchFieldFocused() {
155        return mInput.isFocused();
156    }
157
158    /**
159     * Creates a new market search intent.
160     */
161    public Intent createMarketSearchIntent(String query) {
162        Uri marketSearchUri = Uri.parse("market://search")
163                .buildUpon()
164                .appendQueryParameter("c", "apps")
165                .appendQueryParameter("q", query)
166                .build();
167        return new Intent(Intent.ACTION_VIEW).setData(marketSearchUri);
168    }
169
170    /**
171     * Callback for getting search results.
172     */
173    public interface Callbacks {
174
175        /**
176         * Called when the bounds of the search bar has changed.
177         */
178        void onBoundsChanged(Rect newBounds);
179
180        /**
181         * Called when the search is complete.
182         *
183         * @param apps sorted list of matching components or null if in case of failure.
184         */
185        void onSearchResult(String query, ArrayList<ComponentKey> apps);
186
187        /**
188         * Called when the search results should be cleared.
189         */
190        void clearSearchResult();
191    }
192}