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.search; 17 18 import android.text.Editable; 19 import android.text.SpannableStringBuilder; 20 import android.text.TextUtils; 21 import android.text.TextWatcher; 22 import android.text.style.SuggestionSpan; 23 import android.view.KeyEvent; 24 import android.view.View; 25 import android.view.View.OnFocusChangeListener; 26 import android.view.inputmethod.EditorInfo; 27 import android.widget.TextView; 28 import android.widget.TextView.OnEditorActionListener; 29 30 import com.android.launcher3.ExtendedEditText; 31 import com.android.launcher3.Utilities; 32 import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem; 33 import com.android.launcher3.config.FeatureFlags; 34 import com.android.launcher3.search.SearchAlgorithm; 35 import com.android.launcher3.search.SearchCallback; 36 import com.android.launcher3.views.ActivityContext; 37 38 /** 39 * An interface to a search box that AllApps can command. 40 */ 41 public class AllAppsSearchBarController 42 implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener, 43 OnFocusChangeListener { 44 45 protected ActivityContext mLauncher; 46 protected SearchCallback<AdapterItem> mCallback; 47 protected ExtendedEditText mInput; 48 protected String mQuery; 49 private String[] mTextConversions; 50 51 protected SearchAlgorithm<AdapterItem> mSearchAlgorithm; 52 setVisibility(int visibility)53 public void setVisibility(int visibility) { 54 mInput.setVisibility(visibility); 55 } 56 57 /** 58 * Sets the references to the apps model and the search result callback. 59 */ initialize( SearchAlgorithm<AdapterItem> searchAlgorithm, ExtendedEditText input, ActivityContext launcher, SearchCallback<AdapterItem> callback)60 public final void initialize( 61 SearchAlgorithm<AdapterItem> searchAlgorithm, ExtendedEditText input, 62 ActivityContext launcher, SearchCallback<AdapterItem> callback) { 63 mCallback = callback; 64 mLauncher = launcher; 65 66 mInput = input; 67 mInput.addTextChangedListener(this); 68 mInput.setOnEditorActionListener(this); 69 mInput.setOnBackKeyListener(this); 70 mInput.addOnFocusChangeListener(this); 71 mSearchAlgorithm = searchAlgorithm; 72 } 73 74 @Override beforeTextChanged(CharSequence charSequence, int i, int i1, int i2)75 public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { 76 // Do nothing 77 } 78 79 @Override onTextChanged(CharSequence s, int start, int before, int count)80 public void onTextChanged(CharSequence s, int start, int before, int count) { 81 mTextConversions = extractTextConversions(s); 82 } 83 84 /** 85 * Extract text conversions from composing text and send them for search. 86 */ extractTextConversions(CharSequence text)87 public static String[] extractTextConversions(CharSequence text) { 88 if (text instanceof SpannableStringBuilder) { 89 SpannableStringBuilder spanned = (SpannableStringBuilder) text; 90 SuggestionSpan[] suggestionSpans = 91 spanned.getSpans(0, text.length(), SuggestionSpan.class); 92 if (suggestionSpans != null && suggestionSpans.length > 0) { 93 spanned.removeSpan(suggestionSpans[0]); 94 return suggestionSpans[0].getSuggestions(); 95 } 96 } 97 return null; 98 } 99 100 @Override afterTextChanged(final Editable s)101 public void afterTextChanged(final Editable s) { 102 mQuery = s.toString(); 103 if (mQuery.isEmpty()) { 104 mSearchAlgorithm.cancel(true); 105 mCallback.clearSearchResult(); 106 } else { 107 mSearchAlgorithm.cancel(false); 108 mSearchAlgorithm.doSearch(mQuery, mTextConversions, mCallback); 109 } 110 } 111 refreshSearchResult()112 public void refreshSearchResult() { 113 if (TextUtils.isEmpty(mQuery)) { 114 return; 115 } 116 // If play store continues auto updating an app, we want to show partial result. 117 mSearchAlgorithm.cancel(false); 118 mSearchAlgorithm.doSearch(mQuery, mCallback); 119 } 120 121 @Override onEditorAction(TextView v, int actionId, KeyEvent event)122 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 123 124 if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_GO) { 125 // selectFocusedView should return SearchTargetEvent that is passed onto onClick 126 return mLauncher.getAppsView().getMainAdapterProvider().launchHighlightedItem(); 127 } 128 return false; 129 } 130 131 @Override onBackKey()132 public boolean onBackKey() { 133 // Only hide the search field if there is no query 134 String query = Utilities.trim(mInput.getEditableText().toString()); 135 if (query.isEmpty()) { 136 reset(); 137 return true; 138 } 139 return false; 140 } 141 142 @Override onFocusChange(View view, boolean hasFocus)143 public void onFocusChange(View view, boolean hasFocus) { 144 if (!hasFocus && !FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { 145 mInput.hideKeyboard(); 146 } 147 } 148 149 /** 150 * Resets the search bar state. 151 */ reset()152 public void reset() { 153 mCallback.clearSearchResult(); 154 mInput.reset(); 155 mQuery = null; 156 mInput.removeOnFocusChangeListener(this); 157 } 158 159 /** 160 * Focuses the search field to handle key events. 161 */ focusSearchField()162 public void focusSearchField() { 163 mInput.showKeyboard(true /* shouldFocus */); 164 } 165 166 /** 167 * Returns whether the search field is focused. 168 */ isSearchFieldFocused()169 public boolean isSearchFieldFocused() { 170 return mInput.isFocused(); 171 } 172 } 173