diff options
Diffstat (limited to 'src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java')
-rw-r--r-- | src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java new file mode 100644 index 000000000..f785e4cdb --- /dev/null +++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2017 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.search; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.Selection; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.TextUtils; +import android.text.method.TextKeyListener; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.View; +import android.widget.FrameLayout; + +import com.android.launcher3.ExtendedEditText; +import com.android.launcher3.Launcher; +import com.android.launcher3.R; +import com.android.launcher3.allapps.AllAppsGridAdapter; +import com.android.launcher3.allapps.AllAppsRecyclerView; +import com.android.launcher3.allapps.AlphabeticalAppsList; +import com.android.launcher3.allapps.SearchUiManager; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.discovery.AppDiscoveryItem; +import com.android.launcher3.discovery.AppDiscoveryUpdateState; +import com.android.launcher3.graphics.TintedDrawableSpan; +import com.android.launcher3.util.ComponentKey; + +import java.util.ArrayList; + +/** + * Layout to contain the All-apps search UI. + */ +public class AppsSearchContainerLayout extends FrameLayout + implements SearchUiManager, AllAppsSearchBarController.Callbacks { + + private final Launcher mLauncher; + private final int mMinHeight; + private final AllAppsSearchBarController mSearchBarController; + private final SpannableStringBuilder mSearchQueryBuilder; + private final HeaderElevationController mElevationController; + + private ExtendedEditText mSearchInput; + private AlphabeticalAppsList mApps; + private AllAppsRecyclerView mAppsRecyclerView; + private AllAppsGridAdapter mAdapter; + + public AppsSearchContainerLayout(Context context) { + this(context, null); + } + + public AppsSearchContainerLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public AppsSearchContainerLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + mLauncher = Launcher.getLauncher(context); + mMinHeight = getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_height); + mSearchBarController = new AllAppsSearchBarController(); + mElevationController = new HeaderElevationController(this); + + mSearchQueryBuilder = new SpannableStringBuilder(); + Selection.setSelection(mSearchQueryBuilder, 0); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mSearchInput = findViewById(R.id.search_box_input); + + // Update the hint to contain the icon. + // Prefix the original hint with two spaces. The first space gets replaced by the icon + // using span. The second space is used for a singe space character between the hint + // and the icon. + SpannableString spanned = new SpannableString(" " + mSearchInput.getHint()); + spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search), + 0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE); + mSearchInput.setHint(spanned); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && + !mLauncher.getDeviceProfile().isVerticalBarLayout()) { + getLayoutParams().height = mLauncher.getDragLayer().getInsets().top + mMinHeight; + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + + @Override + public void initialize( + AlphabeticalAppsList appsList, AllAppsRecyclerView recyclerView) { + mApps = appsList; + mAppsRecyclerView = recyclerView; + mAppsRecyclerView.addOnScrollListener(mElevationController); + mAdapter = (AllAppsGridAdapter) mAppsRecyclerView.getAdapter(); + + mSearchBarController.initialize(appsList, mSearchInput, mLauncher, this); + } + + @Override + public void refreshSearchResult() { + mSearchBarController.refreshSearchResult(); + } + + @Override + public void reset() { + mElevationController.reset(); + mSearchBarController.reset(); + } + + @Override + public void preDispatchKeyEvent(KeyEvent event) { + // Determine if the key event was actual text, if so, focus the search bar and then dispatch + // the key normally so that it can process this key event + if (!mSearchBarController.isSearchFieldFocused() && + event.getAction() == KeyEvent.ACTION_DOWN) { + final int unicodeChar = event.getUnicodeChar(); + final boolean isKeyNotWhitespace = unicodeChar > 0 && + !Character.isWhitespace(unicodeChar) && !Character.isSpaceChar(unicodeChar); + if (isKeyNotWhitespace) { + boolean gotKey = TextKeyListener.getInstance().onKeyDown(this, mSearchQueryBuilder, + event.getKeyCode(), event); + if (gotKey && mSearchQueryBuilder.length() > 0) { + mSearchBarController.focusSearchField(); + } + } + } + } + + @Override + public boolean shouldRestoreImeState() { + return !TextUtils.isEmpty(mSearchInput.getText()); + } + + @Override + public void startAppsSearch() { + if (mApps != null) { + mSearchBarController.focusSearchField(); + } + } + + @Override + public void onSearchResult(String query, ArrayList<ComponentKey> apps) { + if (apps != null) { + mApps.setOrderedFilter(apps); + notifyResultChanged(); + mAdapter.setLastSearchQuery(query); + } + } + + @Override + public void clearSearchResult() { + if (mApps.setOrderedFilter(null)) { + notifyResultChanged(); + } + + // Clear the search query + mSearchQueryBuilder.clear(); + mSearchQueryBuilder.clearSpans(); + Selection.setSelection(mSearchQueryBuilder, 0); + } + + @Override + public void onAppDiscoverySearchUpdate( + @Nullable AppDiscoveryItem app, @NonNull AppDiscoveryUpdateState state) { + if (!mLauncher.isDestroyed()) { + mApps.onAppDiscoverySearchUpdate(app, state); + notifyResultChanged(); + } + } + + private void notifyResultChanged() { + mElevationController.reset(); + mAppsRecyclerView.onSearchResultsChanged(); + } + + @Override + public void addOnScrollRangeChangeListener(final OnScrollRangeChangeListener listener) { + mLauncher.getHotseat().addOnLayoutChangeListener(new OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) { + listener.onScrollRangeChanged(top); + } else { + listener.onScrollRangeChanged(bottom); + } + } + }); + } +} |