Simplifying all-apps search box

> Giving the search box a solid background so that it work fine with scrolling
> bottom shadow logic for search box

Bug: 73085356
Change-Id: Ie4dc4922be39ffd8e2d562becedbd4c6f820e6c9
This commit is contained in:
Sunny Goyal 2018-03-01 14:55:22 -08:00
parent dd535466bf
commit dbd6bb348c
13 changed files with 85 additions and 187 deletions

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
<!-- Copyright (C) 2018 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.
@ -13,8 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="?android:attr/colorAccent" />
<size android:height="1dp" />
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="?attr/popupColorPrimary" />
<corners android:radius="2dp" />
</shape>

View File

@ -37,7 +37,7 @@
platform bug, which prevents using custom attributes in <include> tag -->
<include
android:id="@id/search_container_all_apps"
layout="?android:attr/keyboardLayout"/>
layout="@layout/search_container_all_apps"/>
<View
android:id="@+id/nav_bar_bg"

View File

@ -17,53 +17,23 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@id/search_container_all_apps"
android:layout_width="match_parent"
android:layout_height="@dimen/all_apps_search_bar_height"
android:layout_gravity="center|top"
android:layout_marginBottom="-8dp"
android:gravity="center|bottom"
android:paddingLeft="@dimen/dynamic_grid_edge_margin"
android:paddingRight="@dimen/dynamic_grid_edge_margin"
android:saveEnabled="false" >
<!--
Note: The following relation should always be true so that the shadows are aligned properly
search_box_input.layout_marginBottom
== search_divider.layout_marginBottom
== - (search_container.layout_marginBottom)
>= 5dp
-->
<com.android.launcher3.ExtendedEditText
android:id="@+id/search_box_input"
android:layout_width="match_parent"
android:layout_height="@dimen/all_apps_search_bar_field_height"
android:layout_gravity="bottom"
android:layout_marginBottom="8dp"
android:background="@android:color/transparent"
android:focusableInTouchMode="true"
android:gravity="center"
android:hint="@string/all_apps_search_bar_hint"
android:imeOptions="actionSearch|flagNoExtractUi"
android:inputType="text|textNoSuggestions|textCapWords"
android:maxLines="1"
android:saveEnabled="false"
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="?android:attr/textColorSecondary"
android:textColorHint="@drawable/all_apps_search_hint"
android:textSize="16sp" />
<!-- This needs to be a container with a view, to simulate a scrolling effect for the header.
We translate the header down, and its content up by the same amount, so that it gets
clipped by the parent, making it look like the divider was scrolled out of the view. -->
<FrameLayout
android:id="@+id/search_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom"
android:layout_marginBottom="8dp" >
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@drawable/all_apps_search_divider"/>
</FrameLayout>
</com.android.launcher3.allapps.search.AppsSearchContainerLayout>
android:layout_height="@dimen/all_apps_search_bar_field_height"
android:layout_centerHorizontal="true"
android:layout_gravity="top|center_horizontal"
android:layout_marginTop="8dp"
android:background="@drawable/bg_all_apps_searchbox"
android:elevation="1dp"
android:focusableInTouchMode="true"
android:gravity="center"
android:hint="@string/all_apps_search_bar_hint"
android:imeOptions="actionSearch|flagNoExtractUi"
android:inputType="text|textNoSuggestions|textCapWords"
android:maxLines="1"
android:padding="8dp"
android:saveEnabled="false"
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="?android:attr/textColorSecondary"
android:textColorHint="@drawable/all_apps_search_hint"
android:textSize="16sp"
android:translationY="24dp" />

View File

@ -26,7 +26,6 @@
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionModeOverlay">true</item>
<item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
<item name="android:keyboardLayout">@layout/search_container_all_apps</item>
</style>
<!-- Workspace -->

View File

@ -79,7 +79,7 @@
<!-- All Apps -->
<dimen name="all_apps_button_scale_down">0dp</dimen>
<dimen name="all_apps_search_bar_field_height">48dp</dimen>
<dimen name="all_apps_search_bar_height">60dp</dimen>
<dimen name="all_apps_search_bar_bottom_padding">30dp</dimen>
<dimen name="all_apps_empty_search_message_top_offset">40dp</dimen>
<dimen name="all_apps_empty_search_bg_top_offset">144dp</dimen>
<dimen name="all_apps_background_canvas_width">700dp</dimen>

View File

@ -25,7 +25,6 @@
<item name="android:windowShowWallpaper">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:colorEdgeEffect">#FF757575</item>
<item name="android:keyboardLayout">@layout/search_container_all_apps</item>
</style>
<style name="BaseLauncherThemeWithCustomAttrs" parent="@style/BaseLauncherTheme">

View File

@ -204,7 +204,7 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
mHeader.reset();
}
// Reset the search bar and base recycler view after transitioning home
mSearchUiManager.reset();
mSearchUiManager.resetSearch();
}
@Override
@ -291,11 +291,10 @@ public class AllAppsContainerView extends RelativeLayout implements DragSource,
ViewGroup.MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
if (grid.isVerticalBarLayout()) {
mlp.leftMargin = insets.left;
mlp.topMargin = insets.top;
mlp.rightMargin = insets.right;
setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0);
} else {
mlp.leftMargin = mlp.rightMargin = mlp.topMargin = 0;
mlp.leftMargin = mlp.rightMargin = 0;
setPadding(0, 0, 0, 0);
}
setLayoutParams(mlp);

View File

@ -27,7 +27,6 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.android.launcher3.R;
@ -68,6 +67,7 @@ public class FloatingHeaderView extends LinearLayout implements
private int mTranslationY;
private boolean mForwardToRecyclerView;
protected boolean mTabsHidden;
protected int mMaxTranslation;
public FloatingHeaderView(@NonNull Context context) {
@ -85,6 +85,7 @@ public class FloatingHeaderView extends LinearLayout implements
}
public void setup(AllAppsContainerView.AdapterHolder[] mAH, boolean tabsHidden) {
mTabsHidden = tabsHidden;
mTabLayout.setVisibility(tabsHidden ? View.GONE : View.VISIBLE);
mMainRV = setupRV(mMainRV, mAH[AllAppsContainerView.AdapterHolder.MAIN].recyclerView);
mWorkRV = setupRV(mWorkRV, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView);
@ -105,7 +106,13 @@ public class FloatingHeaderView extends LinearLayout implements
}
public int getMaxTranslation() {
return mMaxTranslation;
if (mMaxTranslation == 0 && mTabsHidden) {
return getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_bottom_padding);
} else if (mMaxTranslation > 0 && mTabsHidden) {
return mMaxTranslation + getPaddingTop();
} else {
return mMaxTranslation;
}
}
private boolean canSnapAt(int currentScrollY) {

View File

@ -37,7 +37,7 @@ public interface SearchUiManager {
/**
* Notifies the search manager to close any active search session.
*/
void reset();
void resetSearch();
/**
* Called before dispatching a key event, in case the search manager wants to initialize

View File

@ -15,6 +15,12 @@
*/
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.graphics.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
import android.content.Context;
import android.graphics.Rect;
import android.support.animation.FloatValueHolder;
@ -29,7 +35,7 @@ import android.text.method.TextKeyListener;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.view.ViewGroup.MarginLayoutParams;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ExtendedEditText;
@ -47,20 +53,16 @@ import java.util.ArrayList;
/**
* Layout to contain the All-apps search UI.
*/
public class AppsSearchContainerLayout extends FrameLayout
public class AppsSearchContainerLayout extends ExtendedEditText
implements SearchUiManager, AllAppsSearchBarController.Callbacks,
AllAppsStore.OnUpdateListener {
private final Launcher mLauncher;
private final int mMinHeight;
private final int mSearchBoxHeight;
private final AllAppsSearchBarController mSearchBarController;
private final SpannableStringBuilder mSearchQueryBuilder;
private ExtendedEditText mSearchInput;
private AlphabeticalAppsList mApps;
private View mDivider;
private HeaderElevationController mElevationController;
private AllAppsContainerView mAppsView;
private SpringAnimation mSpring;
@ -76,39 +78,22 @@ public class AppsSearchContainerLayout extends FrameLayout
super(context, attrs, defStyleAttr);
mLauncher = Launcher.getLauncher(context);
mMinHeight = getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_height);
mSearchBoxHeight = getResources()
.getDimensionPixelSize(R.dimen.all_apps_search_bar_field_height);
mSearchBarController = new AllAppsSearchBarController();
mSearchQueryBuilder = new SpannableStringBuilder();
Selection.setSelection(mSearchQueryBuilder, 0);
// Note: This spring does nothing.
mSpring = new SpringAnimation(new FloatValueHolder()).setSpring(new SpringForce(0));
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mSearchInput = findViewById(R.id.search_box_input);
mDivider = findViewById(R.id.search_divider);
mElevationController = new HeaderElevationController(mDivider);
// 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());
SpannableString spanned = new SpannableString(" " + getHint());
spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
mSearchInput.setHint(spanned);
setHint(spanned);
DeviceProfile dp = mLauncher.getDeviceProfile();
if (!dp.isVerticalBarLayout()) {
LayoutParams lp = (LayoutParams) mDivider.getLayoutParams();
lp.leftMargin = lp.rightMargin = dp.edgeMarginPx;
}
// Note: This spring does nothing.
mSpring = new SpringAnimation(new FloatValueHolder()).setSpring(new SpringForce(0));
}
@Override
@ -125,21 +110,39 @@ public class AppsSearchContainerLayout extends FrameLayout
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Update the width to match the grid padding
DeviceProfile dp = mLauncher.getDeviceProfile();
if (!dp.isVerticalBarLayout()) {
getLayoutParams().height = dp.getInsets().top + mMinHeight;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
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);
}
@Override
public void initialize(AllAppsContainerView appsView) {
mApps = appsView.getApps();
mAppsView = appsView;
appsView.addElevationController(mElevationController);
mSearchBarController.initialize(
new DefaultAppSearchAlgorithm(mApps.getApps()), mSearchInput, mLauncher, this);
new DefaultAppSearchAlgorithm(mApps.getApps()), this, mLauncher, this);
}
@Override
@ -153,8 +156,7 @@ public class AppsSearchContainerLayout extends FrameLayout
}
@Override
public void reset() {
mElevationController.reset();
public void resetSearch() {
mSearchBarController.reset();
}
@ -200,7 +202,6 @@ public class AppsSearchContainerLayout extends FrameLayout
}
private void notifyResultChanged() {
mElevationController.reset();
mAppsView.onSearchResultsChanged();
}
@ -214,9 +215,9 @@ public class AppsSearchContainerLayout extends FrameLayout
if (!dp.isVerticalBarLayout()) {
Rect insets = dp.getInsets();
int hotseatBottom = bottom - dp.hotseatBarBottomPaddingPx - insets.bottom;
int searchTopMargin = insets.top + (mMinHeight - mSearchBoxHeight)
+ ((MarginLayoutParams) getLayoutParams()).bottomMargin;
listener.onScrollRangeChanged(hotseatBottom - searchTopMargin);
MarginLayoutParams mlp = ((MarginLayoutParams) getLayoutParams());
int myBot = mlp.topMargin + (int) getTranslationY() + mlp.height;
listener.onScrollRangeChanged(hotseatBottom - myBot);
} else {
listener.onScrollRangeChanged(bottom);
}

View File

@ -1,81 +0,0 @@
package com.android.launcher3.allapps.search;
import android.content.res.Resources;
import android.graphics.Outline;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import com.android.launcher3.BaseRecyclerView;
import com.android.launcher3.R;
/**
* Helper class for controlling the header elevation in response to RecyclerView scroll.
*/
public class HeaderElevationController extends RecyclerView.OnScrollListener {
private final View mHeader;
private final View mHeaderChild;
private final float mMaxElevation;
private final float mScrollToElevation;
private int mCurrentY = 0;
public HeaderElevationController(View header) {
mHeader = header;
final Resources res = mHeader.getContext().getResources();
mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation);
mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
// We need to provide a custom outline so the shadow only appears on the bottom edge.
// The top, left and right edges are all extended out to match parent's edge, so that
// the shadow is clipped by the parent.
final ViewOutlineProvider vop = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
// Set the left and top to be at the parents edge. Since the coordinates are
// relative to this view,
// (x = -view.getLeft()) for this view => (x = 0) for parent
final int left = -view.getLeft();
final int top = -view.getTop();
// Since the view is centered align, the spacing on left and right are same.
// Add same spacing on the right to reach parent's edge.
final int right = view.getWidth() - left;
final int bottom = view.getHeight();
final int offset = (int) mMaxElevation;
outline.setRect(left - offset, top - offset, right + offset, bottom);
}
};
mHeader.setOutlineProvider(vop);
mHeaderChild = ((ViewGroup) mHeader).getChildAt(0);
}
public void reset() {
mCurrentY = 0;
onScroll(mCurrentY);
}
@Override
public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
mCurrentY = ((BaseRecyclerView) recyclerView).getCurrentScrollY();
onScroll(mCurrentY);
}
private void onScroll(int scrollY) {
float elevationPct = Math.min(scrollY, mScrollToElevation) / mScrollToElevation;
float newElevation = mMaxElevation * elevationPct;
if (Float.compare(mHeader.getElevation(), newElevation) != 0) {
mHeader.setElevation(newElevation);
// To simulate a scrolling effect for the header, we translate the header down, and
// its content up by the same amount, so that it gets clipped by the parent, making it
// look like the content was scrolled out of the view.
int shift = Math.min(mHeader.getHeight(), scrollY);
mHeader.setTranslationY(-shift);
mHeaderChild.setTranslationY(shift);
}
}
}

View File

@ -61,6 +61,9 @@ public class IconNormalizer {
private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.005f;
private static final float SCALE_NOT_INITIALIZED = 0;
// Ratio of the diameter of an normalized circular icon to the actual icon size.
public static final float ICON_VISIBLE_AREA_FACTOR = 0.92f;
private final int mMaxSize;
private final Bitmap mBitmap;
private final Bitmap mBitmapARGB;

View File

@ -15,6 +15,7 @@
*/
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.WorkspaceStateTransitionAnimation.NO_ANIM_PROPERTY_SETTER;
import android.content.Context;
@ -174,7 +175,8 @@ public class OverviewPanel extends LinearLayout implements Insettable, View.OnCl
}
private void setState(LauncherState state, PropertySetter setter) {
setter.setViewAlpha(this, 1f - state.getHoseatAlpha(mLauncher), Interpolators.ACCEL);
float myAlpha = state == OVERVIEW ? 1 : 0;
setter.setViewAlpha(this, myAlpha, Interpolators.ACCEL);
}
public static int getButtonBarHeight(Launcher launcher) {