summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/allapps/DefaultAppSearchController.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/allapps/DefaultAppSearchController.java')
-rw-r--r--src/com/android/launcher3/allapps/DefaultAppSearchController.java270
1 files changed, 270 insertions, 0 deletions
diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchController.java b/src/com/android/launcher3/allapps/DefaultAppSearchController.java
new file mode 100644
index 000000000..20924af94
--- /dev/null
+++ b/src/com/android/launcher3/allapps/DefaultAppSearchController.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps;
+
+import android.content.Context;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.TextView;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.Thunk;
+
+import java.util.List;
+
+
+/**
+ * The default search controller.
+ */
+final class DefaultAppSearchController extends AllAppsSearchBarController
+ implements TextWatcher, TextView.OnEditorActionListener, View.OnClickListener {
+
+ private static final boolean ALLOW_SINGLE_APP_LAUNCH = true;
+
+ private static final int FADE_IN_DURATION = 175;
+ private static final int FADE_OUT_DURATION = 100;
+ private static final int SEARCH_TRANSLATION_X_DP = 18;
+
+ private final Context mContext;
+ @Thunk final InputMethodManager mInputMethodManager;
+
+ private DefaultAppSearchAlgorithm mSearchManager;
+
+ private ViewGroup mContainerView;
+ private View mSearchView;
+ @Thunk View mSearchBarContainerView;
+ private View mSearchButtonView;
+ private View mDismissSearchButtonView;
+ @Thunk AllAppsSearchEditView mSearchBarEditView;
+ @Thunk AllAppsRecyclerView mAppsRecyclerView;
+ @Thunk Runnable mFocusRecyclerViewRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mAppsRecyclerView.requestFocus();
+ }
+ };
+
+ public DefaultAppSearchController(Context context, ViewGroup containerView,
+ AllAppsRecyclerView appsRecyclerView) {
+ mContext = context;
+ mInputMethodManager = (InputMethodManager)
+ mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+ mContainerView = containerView;
+ mAppsRecyclerView = appsRecyclerView;
+ }
+
+ @Override
+ public View getView(ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+ mSearchView = inflater.inflate(R.layout.all_apps_search_bar, parent, false);
+ mSearchView.setOnClickListener(this);
+
+ mSearchButtonView = mSearchView.findViewById(R.id.search_button);
+ mSearchBarContainerView = mSearchView.findViewById(R.id.search_container);
+ mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button);
+ mDismissSearchButtonView.setOnClickListener(this);
+ mSearchBarEditView = (AllAppsSearchEditView)
+ mSearchBarContainerView.findViewById(R.id.search_box_input);
+ mSearchBarEditView.addTextChangedListener(this);
+ mSearchBarEditView.setOnEditorActionListener(this);
+ mSearchBarEditView.setOnBackKeyListener(
+ new AllAppsSearchEditView.OnBackKeyListener() {
+ @Override
+ public void onBackKey() {
+ // Only hide the search field if there is no query, or if there
+ // are no filtered results
+ String query = Utilities.trim(
+ mSearchBarEditView.getEditableText().toString());
+ if (query.isEmpty() || mApps.hasNoFilteredResults()) {
+ hideSearchField(true, mFocusRecyclerViewRunnable);
+ }
+ }
+ });
+ return mSearchView;
+ }
+
+ @Override
+ public void focusSearchField() {
+ mSearchBarEditView.requestFocus();
+ showSearchField();
+ }
+
+ @Override
+ public boolean isSearchFieldFocused() {
+ return mSearchBarEditView.isFocused();
+ }
+
+ @Override
+ protected void onInitialize() {
+ mSearchManager = new DefaultAppSearchAlgorithm(mApps.getApps());
+ }
+
+ @Override
+ public void reset() {
+ hideSearchField(false, null);
+ }
+
+ @Override
+ public boolean shouldShowPredictionBar() {
+ // Keep showing the prediction bar if the input query is empty
+ return mSearchBarEditView.getEditableText().toString().isEmpty();
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v == mSearchView) {
+ showSearchField();
+ } else if (v == mDismissSearchButtonView) {
+ hideSearchField(true, mFocusRecyclerViewRunnable);
+ }
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // Do nothing
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ // Do nothing
+ }
+
+ @Override
+ public void afterTextChanged(final Editable s) {
+ String query = s.toString();
+ if (query.isEmpty()) {
+ mSearchManager.cancel(true);
+ mCb.clearSearchResult();
+ } else {
+ mSearchManager.cancel(false);
+ mSearchManager.doSearch(query, mCb);
+ }
+ }
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ // Skip if we disallow app-launch-on-enter
+ if (!ALLOW_SINGLE_APP_LAUNCH) {
+ return false;
+ }
+ // Skip if it's not the right action
+ if (actionId != EditorInfo.IME_ACTION_DONE) {
+ return false;
+ }
+ // Skip if there isn't exactly one item
+ if (mApps.getSize() != 1) {
+ return false;
+ }
+ // If there is exactly one icon, then quick-launch it
+ List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
+ for (int i = 0; i < items.size(); i++) {
+ AlphabeticalAppsList.AdapterItem item = items.get(i);
+ if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) {
+ mAppsRecyclerView.getChildAt(i).performClick();
+ mInputMethodManager.hideSoftInputFromWindow(
+ mContainerView.getWindowToken(), 0);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Focuses the search field.
+ */
+ private void showSearchField() {
+ // Show the search bar and focus the search
+ final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP,
+ mContext.getResources().getDisplayMetrics());
+ mSearchBarContainerView.setVisibility(View.VISIBLE);
+ mSearchBarContainerView.setAlpha(0f);
+ mSearchBarContainerView.setTranslationX(translationX);
+ mSearchBarContainerView.animate()
+ .alpha(1f)
+ .translationX(0)
+ .setDuration(FADE_IN_DURATION)
+ .withLayer()
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mSearchBarEditView.requestFocus();
+ mInputMethodManager.showSoftInput(mSearchBarEditView,
+ InputMethodManager.SHOW_IMPLICIT);
+ }
+ });
+ mSearchButtonView.animate()
+ .alpha(0f)
+ .translationX(-translationX)
+ .setDuration(FADE_OUT_DURATION)
+ .withLayer();
+ }
+
+ /**
+ * Unfocuses the search field.
+ */
+ @Thunk void hideSearchField(boolean animated, final Runnable postAnimationRunnable) {
+ mSearchManager.cancel(true);
+
+ final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0;
+ final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP,
+ mContext.getResources().getDisplayMetrics());
+ if (animated) {
+ // Hide the search bar and focus the recycler view
+ mSearchBarContainerView.animate()
+ .alpha(0f)
+ .translationX(0)
+ .setDuration(FADE_IN_DURATION)
+ .withLayer()
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mSearchBarContainerView.setVisibility(View.INVISIBLE);
+ if (resetTextField) {
+ mSearchBarEditView.setText("");
+ }
+ mCb.clearSearchResult();
+ if (postAnimationRunnable != null) {
+ postAnimationRunnable.run();
+ }
+ }
+ });
+ mSearchButtonView.setTranslationX(-translationX);
+ mSearchButtonView.animate()
+ .alpha(1f)
+ .translationX(0)
+ .setDuration(FADE_OUT_DURATION)
+ .withLayer();
+ } else {
+ mSearchBarContainerView.setVisibility(View.INVISIBLE);
+ if (resetTextField) {
+ mSearchBarEditView.setText("");
+ }
+ mCb.clearSearchResult();
+ mSearchButtonView.setAlpha(1f);
+ mSearchButtonView.setTranslationX(0f);
+ if (postAnimationRunnable != null) {
+ postAnimationRunnable.run();
+ }
+ }
+ mInputMethodManager.hideSoftInputFromWindow(mContainerView.getWindowToken(), 0);
+ }
+}