mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-03-02 08:56:55 +00:00
Refactoring all apps search to support external search bar.
- Adding support for an external search bar that can be used to search a container view. This adds a new interface AllAppsSearchController which manages the external search bar. Each controller will have its own search implementation which means that we no longer need a common AppSearchManager interface. - Removing elevation controller as we no longer have a builtin search bar in all apps - Refactoring container view insets so that they behave the same in all containers. - Refactoring apps view to ensure that we only update the number of columns with the available width - Cleaning up LauncherCallbacks interface Bug: 20127840 Bug: 21494973 Change-Id: I710b8e18196961d77d8a29f0c345531d480936fe
This commit is contained in:
@@ -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;
|
||||
private 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user