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.support.annotation.NonNull; 23 import android.support.annotation.Nullable; 24 import android.text.Editable; 25 import android.text.TextUtils; 26 import android.text.TextWatcher; 27 import android.view.KeyEvent; 28 import android.view.View; 29 import android.view.inputmethod.EditorInfo; 30 import android.view.inputmethod.InputMethodManager; 31 import android.widget.TextView; 32 import android.widget.TextView.OnEditorActionListener; 33 34 import com.android.launcher3.ExtendedEditText; 35 import com.android.launcher3.Launcher; 36 import com.android.launcher3.Utilities; 37 import com.android.launcher3.discovery.AppDiscoveryItem; 38 import com.android.launcher3.discovery.AppDiscoveryUpdateState; 39 import com.android.launcher3.util.ComponentKey; 40 41 import java.util.ArrayList; 42 43 /** 44 * An interface to a search box that AllApps can command. 45 */ 46 public abstract class AllAppsSearchBarController 47 implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener { 48 49 protected Launcher mLauncher; 50 protected AlphabeticalAppsList mApps; 51 protected Callbacks mCb; 52 protected ExtendedEditText mInput; 53 protected String mQuery; 54 55 protected DefaultAppSearchAlgorithm mSearchAlgorithm; 56 protected InputMethodManager mInputMethodManager; 57 58 public void setVisibility(int visibility) { 59 mInput.setVisibility(visibility); 60 } 61 /** 62 * Sets the references to the apps model and the search result callback. 63 */ 64 public final void initialize( 65 AlphabeticalAppsList apps, ExtendedEditText input, 66 Launcher launcher, Callbacks cb) { 67 mApps = apps; 68 mCb = cb; 69 mLauncher = launcher; 70 71 mInput = input; 72 mInput.addTextChangedListener(this); 73 mInput.setOnEditorActionListener(this); 74 mInput.setOnBackKeyListener(this); 75 76 mInputMethodManager = (InputMethodManager) 77 mInput.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 78 79 mSearchAlgorithm = onInitializeSearch(); 80 81 onInitialized(); 82 } 83 84 /** 85 * You can override this method to perform custom initialization. 86 */ 87 protected void onInitialized() { 88 } 89 90 /** 91 * To be implemented by subclasses. This method will get called when the controller is set. 92 */ 93 protected abstract DefaultAppSearchAlgorithm onInitializeSearch(); 94 95 @Override 96 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 97 // Do nothing 98 } 99 100 @Override 101 public void onTextChanged(CharSequence s, int start, int before, int count) { 102 // Do nothing 103 } 104 105 @Override 106 public void afterTextChanged(final Editable s) { 107 mQuery = s.toString(); 108 if (mQuery.isEmpty()) { 109 mSearchAlgorithm.cancel(true); 110 mCb.clearSearchResult(); 111 } else { 112 mSearchAlgorithm.cancel(false); 113 mSearchAlgorithm.doSearch(mQuery, mCb); 114 } 115 } 116 117 protected void refreshSearchResult() { 118 if (TextUtils.isEmpty(mQuery)) { 119 return; 120 } 121 // If play store continues auto updating an app, we want to show partial result. 122 mSearchAlgorithm.cancel(false); 123 mSearchAlgorithm.doSearch(mQuery, mCb); 124 } 125 126 @Override 127 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 128 // Skip if it's not the right action 129 if (actionId != EditorInfo.IME_ACTION_SEARCH) { 130 return false; 131 } 132 133 // Skip if the query is empty 134 String query = v.getText().toString(); 135 if (query.isEmpty()) { 136 return false; 137 } 138 return mLauncher.startActivitySafely(v, createMarketSearchIntent(query), null); 139 } 140 141 @Override 142 public boolean onBackKey() { 143 // Only hide the search field if there is no query 144 String query = Utilities.trim(mInput.getEditableText().toString()); 145 if (query.isEmpty()) { 146 reset(); 147 return true; 148 } 149 return false; 150 } 151 152 /** 153 * Resets the search bar state. 154 */ 155 public void reset() { 156 unfocusSearchField(); 157 mCb.clearSearchResult(); 158 mInput.setText(""); 159 mQuery = null; 160 hideKeyboard(); 161 } 162 163 protected void hideKeyboard() { 164 mInputMethodManager.hideSoftInputFromWindow(mInput.getWindowToken(), 0); 165 } 166 167 protected void unfocusSearchField() { 168 View nextFocus = mInput.focusSearch(View.FOCUS_DOWN); 169 if (nextFocus != null) { 170 nextFocus.requestFocus(); 171 } 172 } 173 174 /** 175 * Focuses the search field to handle key events. 176 */ 177 public void focusSearchField() { 178 mInput.showKeyboard(); 179 } 180 181 /** 182 * Returns whether the search field is focused. 183 */ 184 public boolean isSearchFieldFocused() { 185 return mInput.isFocused(); 186 } 187 188 /** 189 * Creates a new market search intent. 190 */ 191 public Intent createMarketSearchIntent(String query) { 192 Uri marketSearchUri = Uri.parse("market://search") 193 .buildUpon() 194 .appendQueryParameter("c", "apps") 195 .appendQueryParameter("q", query) 196 .build(); 197 return new Intent(Intent.ACTION_VIEW).setData(marketSearchUri); 198 } 199 200 /** 201 * Callback for getting search results. 202 */ 203 public interface Callbacks { 204 205 /** 206 * Called when the bounds of the search bar has changed. 207 */ 208 @Deprecated 209 void onBoundsChanged(Rect newBounds); 210 211 /** 212 * Called when the search is complete. 213 * 214 * @param apps sorted list of matching components or null if in case of failure. 215 */ 216 void onSearchResult(String query, ArrayList<ComponentKey> apps); 217 218 /** 219 * Called when the search results should be cleared. 220 */ 221 void clearSearchResult(); 222 223 224 /** 225 * Called when the app discovery is providing an update of search, which can either be 226 * START for starting a new discovery, 227 * UPDATE for providing a new search result, can be called multiple times, 228 * END for indicating the end of results. 229 * 230 * @param app result item if UPDATE, else null 231 * @param app the update state, START, UPDATE or END 232 */ 233 void onAppDiscoverySearchUpdate(@Nullable AppDiscoveryItem app, 234 @NonNull AppDiscoveryUpdateState state); 235 } 236 237 }