Files
lawnchair/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
Tony Wickham b87f3cdc1c Add support for having more hotseat icons in the DB than we show
Split InvariantDeviceProfile#numHotseatIcons into two variables:
numDatabaseHotseatIcons and numShownHotseatIcons. These are generally
the same, but different DisplayOptions within the same GridOption
can choose to show different numbers of hotseat icons while sharing
the same database.

numDatabaseHotseatIcons is used for all reading/writing/migrating
purposes, while numShownHotseatIcons determines how many Hotseat
icons to show in the UI.

Test: Existing tests pass, added two new migration tests
Bug: 184789479
Bug: 171917176

Change-Id: I54583504f61a47a4444b6a637ebb7e3ab31528b7
2021-04-12 16:08:15 -07:00

216 lines
7.6 KiB
Java

/*
* 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 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.AllAppsGridAdapter.AdapterItem;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AlphabeticalAppsList;
import com.android.launcher3.allapps.SearchUiManager;
import com.android.launcher3.search.SearchCallback;
import java.util.ArrayList;
/**
* Layout to contain the All-apps search UI.
*/
public class AppsSearchContainerLayout extends ExtendedEditText
implements SearchUiManager, SearchCallback<AdapterItem>,
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.cellLayoutBorderSpacingPx,
dp.numShownHotseatIcons);
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 initializeSearch(AllAppsContainerView appsView) {
mApps = appsView.getApps();
mAppsView = appsView;
mSearchBarController.initialize(
new DefaultAppSearchAlgorithm(mLauncher),
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<AdapterItem> items) {
if (items != null) {
mApps.setSearchResults(items);
notifyResultChanged();
mAppsView.setLastSearchQuery(query);
}
}
@Override
public void onAppendSearchResult(String query, ArrayList<AdapterItem> items) {
if (items != null) {
mApps.appendSearchResults(items);
notifyResultChanged();
}
}
@Override
public void clearSearchResult() {
if (mApps.setSearchResults(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 ExtendedEditText getEditText() {
return this;
}
}