Home | History | Annotate | Download | only in allapps
      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 }