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 */ 16 package com.android.launcher3.allapps; 17 18 import android.content.Context; 19 import android.content.Intent; 20 import android.graphics.Rect; 21 import android.net.Uri; 22 import android.text.Editable; 23 import android.text.TextWatcher; 24 import android.view.KeyEvent; 25 import android.view.View; 26 import android.view.inputmethod.EditorInfo; 27 import android.view.inputmethod.InputMethodManager; 28 import android.widget.TextView; 29 import android.widget.TextView.OnEditorActionListener; 30 31 import com.android.launcher3.ExtendedEditText; 32 import com.android.launcher3.Launcher; 33 import com.android.launcher3.Utilities; 34 import com.android.launcher3.util.ComponentKey; 35 36 import java.util.ArrayList; 37 38 /** 39 * An interface to a search box that AllApps can command. 40 */ 41 public 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 */ initialize( AlphabeticalAppsList apps, ExtendedEditText input, Launcher launcher, Callbacks cb)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 */ onInitializeSearch()76 protected abstract DefaultAppSearchAlgorithm onInitializeSearch(); 77 78 @Override beforeTextChanged(CharSequence s, int start, int count, int after)79 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 80 // Do nothing 81 } 82 83 @Override onTextChanged(CharSequence s, int start, int before, int count)84 public void onTextChanged(CharSequence s, int start, int before, int count) { 85 // Do nothing 86 } 87 88 @Override afterTextChanged(final Editable s)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 onEditorAction(TextView v, int actionId, KeyEvent event)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 onBackKey()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 */ reset()129 public void reset() { 130 unfocusSearchField(); 131 mCb.clearSearchResult(); 132 mInput.setText(""); 133 mInputMethodManager.hideSoftInputFromWindow(mInput.getWindowToken(), 0); 134 } 135 unfocusSearchField()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 */ focusSearchField()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 */ isSearchFieldFocused()154 public boolean isSearchFieldFocused() { 155 return mInput.isFocused(); 156 } 157 158 /** 159 * Creates a new market search intent. 160 */ createMarketSearchIntent(String query)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 */ onBoundsChanged(Rect newBounds)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 */ onSearchResult(String query, ArrayList<ComponentKey> apps)185 void onSearchResult(String query, ArrayList<ComponentKey> apps); 186 187 /** 188 * Called when the search results should be cleared. 189 */ clearSearchResult()190 void clearSearchResult(); 191 } 192 }