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.TextUtils; 24 import android.text.TextWatcher; 25 import android.view.KeyEvent; 26 import android.view.View; 27 import android.view.inputmethod.EditorInfo; 28 import android.view.inputmethod.InputMethodManager; 29 import android.widget.TextView; 30 import android.widget.TextView.OnEditorActionListener; 31 32 import com.android.launcher3.ExtendedEditText; 33 import com.android.launcher3.Launcher; 34 import com.android.launcher3.Utilities; 35 import com.android.launcher3.util.ComponentKey; 36 37 import java.util.ArrayList; 38 39 /** 40 * An interface to a search box that AllApps can command. 41 */ 42 public 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 131 String query = Utilities.trim(mInput.getEditableText().toString()); 132 if (query.isEmpty()) { 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 mQuery = null; 147 hideKeyboard(); 148 } 149 150 protected void hideKeyboard() { 151 mInputMethodManager.hideSoftInputFromWindow(mInput.getWindowToken(), 0); 152 } 153 154 protected void unfocusSearchField() { 155 View nextFocus = mInput.focusSearch(View.FOCUS_DOWN); 156 if (nextFocus != null) { 157 nextFocus.requestFocus(); 158 } 159 } 160 161 /** 162 * Focuses the search field to handle key events. 163 */ 164 public void focusSearchField() { 165 mInput.showKeyboard(); 166 } 167 168 /** 169 * Returns whether the search field is focused. 170 */ 171 public boolean isSearchFieldFocused() { 172 return mInput.isFocused(); 173 } 174 175 /** 176 * Creates a new market search intent. 177 */ 178 public Intent createMarketSearchIntent(String query) { 179 Uri marketSearchUri = Uri.parse("market://search") 180 .buildUpon() 181 .appendQueryParameter("c", "apps") 182 .appendQueryParameter("q", query) 183 .build(); 184 return new Intent(Intent.ACTION_VIEW).setData(marketSearchUri); 185 } 186 187 /** 188 * Callback for getting search results. 189 */ 190 public interface Callbacks { 191 192 /** 193 * Called when the bounds of the search bar has changed. 194 */ 195 @Deprecated 196 void onBoundsChanged(Rect newBounds); 197 198 /** 199 * Called when the search is complete. 200 * 201 * @param apps sorted list of matching components or null if in case of failure. 202 */ 203 void onSearchResult(String query, ArrayList<ComponentKey> apps); 204 205 /** 206 * Called when the search results should be cleared. 207 */ 208 void clearSearchResult(); 209 } 210 }