| /* |
| * 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 static android.view.View.MeasureSpec.EXACTLY; |
| import static android.view.View.MeasureSpec.getSize; |
| import static android.view.View.MeasureSpec.makeMeasureSpec; |
| |
| import static com.android.launcher3.Utilities.prefixTextWithIcon; |
| import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR; |
| |
| import android.content.Context; |
| import android.graphics.Rect; |
| import android.text.Selection; |
| import android.text.SpannableStringBuilder; |
| import android.text.method.TextKeyListener; |
| import android.util.AttributeSet; |
| import android.view.KeyEvent; |
| import android.view.View; |
| import android.view.ViewGroup.MarginLayoutParams; |
| import android.view.animation.Interpolator; |
| import android.widget.EditText; |
| |
| import com.android.launcher3.BaseDraggingActivity; |
| import com.android.launcher3.DeviceProfile; |
| import com.android.launcher3.ExtendedEditText; |
| import com.android.launcher3.Insettable; |
| import com.android.launcher3.R; |
| import com.android.launcher3.allapps.AllAppsContainerView; |
| import com.android.launcher3.allapps.AllAppsStore; |
| import com.android.launcher3.allapps.AlphabeticalAppsList; |
| import com.android.launcher3.allapps.SearchUiManager; |
| import com.android.launcher3.anim.PropertySetter; |
| import com.android.launcher3.util.ComponentKey; |
| |
| import java.util.ArrayList; |
| |
| /** |
| * Layout to contain the All-apps search UI. |
| */ |
| public class AppsSearchContainerLayout extends ExtendedEditText |
| implements SearchUiManager, AllAppsSearchBarController.Callbacks, |
| AllAppsStore.OnUpdateListener, Insettable { |
| |
| private final BaseDraggingActivity mLauncher; |
| private final AllAppsSearchBarController mSearchBarController; |
| private final SpannableStringBuilder mSearchQueryBuilder; |
| |
| private AlphabeticalAppsList mApps; |
| private AllAppsContainerView mAppsView; |
| |
| // The amount of pixels to shift down and overlap with the rest of the content. |
| private final int mContentOverlap; |
| |
| 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 = BaseDraggingActivity.fromContext(context); |
| mSearchBarController = new AllAppsSearchBarController(); |
| |
| mSearchQueryBuilder = new SpannableStringBuilder(); |
| Selection.setSelection(mSearchQueryBuilder, 0); |
| setHint(prefixTextWithIcon(getContext(), R.drawable.ic_allapps_search, getHint())); |
| |
| mContentOverlap = |
| getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_field_height) / 2; |
| } |
| |
| @Override |
| protected void onAttachedToWindow() { |
| super.onAttachedToWindow(); |
| mAppsView.getAppsStore().addUpdateListener(this); |
| } |
| |
| @Override |
| protected void onDetachedFromWindow() { |
| super.onDetachedFromWindow(); |
| mAppsView.getAppsStore().removeUpdateListener(this); |
| } |
| |
| @Override |
| protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
| // Update the width to match the grid padding |
| DeviceProfile dp = mLauncher.getDeviceProfile(); |
| int myRequestedWidth = getSize(widthMeasureSpec); |
| int rowWidth = myRequestedWidth - mAppsView.getActiveRecyclerView().getPaddingLeft() |
| - mAppsView.getActiveRecyclerView().getPaddingRight(); |
| |
| int cellWidth = DeviceProfile.calculateCellWidth(rowWidth, dp.inv.numHotseatIcons); |
| int iconVisibleSize = Math.round(ICON_VISIBLE_AREA_FACTOR * dp.iconSizePx); |
| int iconPadding = cellWidth - iconVisibleSize; |
| |
| int myWidth = rowWidth - iconPadding + getPaddingLeft() + getPaddingRight(); |
| super.onMeasure(makeMeasureSpec(myWidth, EXACTLY), heightMeasureSpec); |
| } |
| |
| @Override |
| protected void onLayout(boolean changed, int left, int top, int right, int bottom) { |
| super.onLayout(changed, left, top, right, bottom); |
| |
| // Shift the widget horizontally so that its centered in the parent (b/63428078) |
| View parent = (View) getParent(); |
| int availableWidth = parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight(); |
| int myWidth = right - left; |
| int expectedLeft = parent.getPaddingLeft() + (availableWidth - myWidth) / 2; |
| int shift = expectedLeft - left; |
| setTranslationX(shift); |
| |
| offsetTopAndBottom(mContentOverlap); |
| } |
| |
| @Override |
| public void initialize(AllAppsContainerView appsView) { |
| mApps = appsView.getApps(); |
| mAppsView = appsView; |
| mSearchBarController.initialize( |
| new DefaultAppSearchAlgorithm(mApps.getApps()), this, mLauncher, this); |
| } |
| |
| @Override |
| public void onAppsUpdated() { |
| mSearchBarController.refreshSearchResult(); |
| } |
| |
| @Override |
| public void resetSearch() { |
| 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 void onSearchResult(String query, ArrayList<ComponentKey> apps) { |
| if (apps != null) { |
| mApps.setOrderedFilter(apps); |
| notifyResultChanged(); |
| mAppsView.setLastSearchQuery(query); |
| } |
| } |
| |
| @Override |
| public void clearSearchResult() { |
| if (mApps.setOrderedFilter(null)) { |
| notifyResultChanged(); |
| } |
| |
| // Clear the search query |
| mSearchQueryBuilder.clear(); |
| mSearchQueryBuilder.clearSpans(); |
| Selection.setSelection(mSearchQueryBuilder, 0); |
| mAppsView.onClearSearchResult(); |
| } |
| |
| private void notifyResultChanged() { |
| mAppsView.onSearchResultsChanged(); |
| } |
| |
| @Override |
| public void setInsets(Rect insets) { |
| MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams(); |
| mlp.topMargin = insets.top; |
| requestLayout(); |
| } |
| |
| @Override |
| public float getScrollRangeDelta(Rect insets) { |
| if (mLauncher.getDeviceProfile().isVerticalBarLayout()) { |
| return 0; |
| } else { |
| return insets.bottom + insets.top; |
| } |
| } |
| |
| @Override |
| public void setContentVisibility(int visibleElements, PropertySetter setter, |
| Interpolator interpolator) { |
| setter.setViewAlpha(this, isQsbVisible(visibleElements) ? 1 : 0, interpolator); |
| } |
| |
| @Override |
| public EditText setTextSearchEnabled(boolean isEnabled) { |
| return this; |
| } |
| } |