From d1bacf55db6b81e5f62f506c69bc7adc8a297958 Mon Sep 17 00:00:00 2001 From: raajkumars Date: Mon, 21 Nov 2022 12:41:34 -0500 Subject: [PATCH] [M3][SearchBar] Added new SearchBar and SearchView components PiperOrigin-RevId: 490002827 --- .../assets/search/search-bar-anatomy.png | Bin 0 -> 78006 bytes .../assets/search/search-bar-light.png | Bin 0 -> 19042 bytes .../assets/search/search-view-anatomy.png | Bin 0 -> 61762 bytes lib/build.gradle | 1 + .../dialog/res/values/themes_base.xml | 6 + .../android/material/search/SearchBar.java | 873 ++++++++++++++++ .../search/SearchBarAnimationHelper.java | 423 ++++++++ .../android/material/search/SearchView.java | 942 ++++++++++++++++++ .../search/SearchViewAnimationHelper.java | 596 +++++++++++ .../search/res-public/values/public.xml | 40 + .../res/drawable/ic_arrow_back_black_24.xml | 26 + .../search/res/drawable/ic_clear_black_24.xml | 28 + .../res/drawable/ic_search_black_24.xml | 26 + .../search/res/layout/mtrl_search_bar.xml | 25 + .../search/res/layout/mtrl_search_view.xml | 125 +++ .../material/search/res/values/attrs.xml | 96 ++ .../material/search/res/values/dimens.xml | 31 + .../material/search/res/values/strings.xml | 39 + .../material/search/res/values/styles.xml | 97 ++ .../material/search/res/values/tokens.xml | 73 ++ .../material/theme/res/values/themes_base.xml | 4 + .../android/material/theme/ThemeTest.java | 2 + 22 files changed, 3453 insertions(+) create mode 100644 docs/components/assets/search/search-bar-anatomy.png create mode 100644 docs/components/assets/search/search-bar-light.png create mode 100644 docs/components/assets/search/search-view-anatomy.png create mode 100644 lib/java/com/google/android/material/search/SearchBar.java create mode 100644 lib/java/com/google/android/material/search/SearchBarAnimationHelper.java create mode 100644 lib/java/com/google/android/material/search/SearchView.java create mode 100644 lib/java/com/google/android/material/search/SearchViewAnimationHelper.java create mode 100644 lib/java/com/google/android/material/search/res-public/values/public.xml create mode 100644 lib/java/com/google/android/material/search/res/drawable/ic_arrow_back_black_24.xml create mode 100644 lib/java/com/google/android/material/search/res/drawable/ic_clear_black_24.xml create mode 100644 lib/java/com/google/android/material/search/res/drawable/ic_search_black_24.xml create mode 100644 lib/java/com/google/android/material/search/res/layout/mtrl_search_bar.xml create mode 100644 lib/java/com/google/android/material/search/res/layout/mtrl_search_view.xml create mode 100644 lib/java/com/google/android/material/search/res/values/attrs.xml create mode 100644 lib/java/com/google/android/material/search/res/values/dimens.xml create mode 100644 lib/java/com/google/android/material/search/res/values/strings.xml create mode 100644 lib/java/com/google/android/material/search/res/values/styles.xml create mode 100644 lib/java/com/google/android/material/search/res/values/tokens.xml diff --git a/docs/components/assets/search/search-bar-anatomy.png b/docs/components/assets/search/search-bar-anatomy.png new file mode 100644 index 0000000000000000000000000000000000000000..8f0817b8872b39a6d9743812f3b9093cbbd24395 GIT binary patch literal 78006 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

U=rE;?fk$iL@ihR=^5L|la(GRzGazEawu5P;}| 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 literal 0 HcmV?d00001 diff --git a/lib/build.gradle b/lib/build.gradle index cf5ec110c1f..da9db03dcf6 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -70,6 +70,7 @@ def srcDirs = [ 'com/google/android/material/radiobutton', 'com/google/android/material/resources', 'com/google/android/material/ripple', + 'com/google/android/material/search', 'com/google/android/material/shape', 'com/google/android/material/shadow', 'com/google/android/material/sidesheet', diff --git a/lib/java/com/google/android/material/dialog/res/values/themes_base.xml b/lib/java/com/google/android/material/dialog/res/values/themes_base.xml index 162331e907f..cfc917798c2 100644 --- a/lib/java/com/google/android/material/dialog/res/values/themes_base.xml +++ b/lib/java/com/google/android/material/dialog/res/values/themes_base.xml @@ -126,6 +126,8 @@ @style/Widget.Material3.NavigationRailView @style/Widget.Material3.NavigationView @style/Widget.Material3.CompoundButton.RadioButton + @style/Widget.Material3.SearchBar + @style/Widget.Material3.SearchView @style/Widget.Material3.Slider @style/Widget.Material3.Snackbar @style/Widget.Material3.Button.TextButton.Snackbar @@ -363,6 +365,8 @@ @style/Widget.Material3.NavigationRailView @style/Widget.Material3.NavigationView @style/Widget.Material3.CompoundButton.RadioButton + @style/Widget.Material3.SearchBar + @style/Widget.Material3.SearchView @style/Widget.Material3.Slider @style/Widget.Material3.Snackbar @style/Widget.Material3.Button.TextButton.Snackbar @@ -527,6 +531,8 @@ @style/Widget.MaterialComponents.NavigationView @style/Widget.MaterialComponents.PopupMenu @style/Widget.MaterialComponents.CompoundButton.RadioButton + @style/Widget.Material3.SearchBar + @style/Widget.Material3.SearchView @style/Widget.MaterialComponents.Snackbar @style/Widget.MaterialComponents.Button.TextButton.Snackbar @style/Widget.MaterialComponents.Snackbar.TextView diff --git a/lib/java/com/google/android/material/search/SearchBar.java b/lib/java/com/google/android/material/search/SearchBar.java new file mode 100644 index 00000000000..c35ac1a7ff8 --- /dev/null +++ b/lib/java/com/google/android/material/search/SearchBar.java @@ -0,0 +1,873 @@ +/* + * Copyright 2022 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 + * + * https://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.google.android.material.search; + +import com.google.android.material.R; + +import static com.google.android.material.theme.overlay.MaterialThemeOverlay.wrap; + +import android.animation.AnimatorListenerAdapter; +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.RippleDrawable; +import android.os.Build; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.os.Parcel; +import android.os.Parcelable; +import androidx.appcompat.content.res.AppCompatResources; +import androidx.appcompat.widget.ActionMenuView; +import androidx.appcompat.widget.Toolbar; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.TextView; +import androidx.annotation.ColorInt; +import androidx.annotation.Dimension; +import androidx.annotation.MenuRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.annotation.StringRes; +import androidx.annotation.StyleRes; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.core.graphics.drawable.DrawableCompat; +import androidx.core.view.MarginLayoutParamsCompat; +import androidx.core.view.ViewCompat; +import androidx.core.widget.TextViewCompat; +import androidx.customview.view.AbsSavedState; +import com.google.android.material.appbar.AppBarLayout; +import com.google.android.material.color.MaterialColors; +import com.google.android.material.internal.ThemeEnforcement; +import com.google.android.material.internal.ToolbarUtils; +import com.google.android.material.shape.MaterialShapeDrawable; +import com.google.android.material.shape.MaterialShapeUtils; +import com.google.android.material.shape.ShapeAppearanceModel; +import com.google.errorprone.annotations.CanIgnoreReturnValue; + +/** + * The {@link SearchBar} represents a floating search field with affordances for search and + * navigation. + * + *

Note: {@link SearchBar} does not support the {@link #setTitle} and {@link #setSubtitle} + * methods, or their corresponding xml attributes. Instead, use {@link #setHint} or {@link + * #setText}, or their corresponding xml attributes, to provide a text affordance for your {@link + * SearchBar}. + */ +public class SearchBar extends Toolbar { + + private static final int DEF_STYLE_RES = R.style.Widget_Material3_SearchBar; + + private static final int DEFAULT_SCROLL_FLAGS = + AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL + | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS + | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP + | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP_MARGINS; + private static final String NAMESPACE_APP = "http://schemas.android.com/apk/res-auto"; + + private final TextView textView; + private final boolean layoutInflated; + private final boolean defaultMarginsEnabled; + private final SearchBarAnimationHelper searchBarAnimationHelper; + private final Drawable defaultNavigationIcon; + private final boolean tintNavigationIcon; + private final boolean forceDefaultNavigationOnClickListener; + + @Nullable private View centerView; + @Nullable private Integer navigationIconTint; + @Nullable private Drawable originalNavigationIconBackground; + private int menuResId = -1; + private boolean defaultScrollFlagsEnabled; + private MaterialShapeDrawable backgroundShape; + + public SearchBar(@NonNull Context context) { + this(context, null); + } + + public SearchBar(@NonNull Context context, @Nullable AttributeSet attrs) { + this(context, attrs, R.attr.searchBarStyle); + } + + public SearchBar(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(wrap(context, attrs, defStyleAttr, DEF_STYLE_RES), attrs, defStyleAttr); + // Ensure we are using the correctly themed context rather than the context that was passed in. + context = getContext(); + validateAttributes(attrs); + + defaultNavigationIcon = + AppCompatResources.getDrawable(context, R.drawable.ic_search_black_24); + searchBarAnimationHelper = new SearchBarAnimationHelper(); + + TypedArray a = + ThemeEnforcement.obtainStyledAttributes( + context, attrs, R.styleable.SearchBar, defStyleAttr, DEF_STYLE_RES); + + ShapeAppearanceModel shapeAppearanceModel = + ShapeAppearanceModel.builder(context, attrs, defStyleAttr, DEF_STYLE_RES).build(); + float elevation = a.getDimension(R.styleable.SearchBar_elevation, 0); + defaultMarginsEnabled = a.getBoolean(R.styleable.SearchBar_defaultMarginsEnabled, true); + defaultScrollFlagsEnabled = a.getBoolean(R.styleable.SearchBar_defaultScrollFlagsEnabled, true); + boolean hideNavigationIcon = a.getBoolean(R.styleable.SearchBar_hideNavigationIcon, false); + forceDefaultNavigationOnClickListener = + a.getBoolean(R.styleable.SearchBar_forceDefaultNavigationOnClickListener, false); + tintNavigationIcon = a.getBoolean(R.styleable.SearchBar_tintNavigationIcon, true); + if (a.hasValue(R.styleable.SearchBar_navigationIconTint)) { + navigationIconTint = a.getColor(R.styleable.SearchBar_navigationIconTint, -1); + } + int textAppearanceResId = a.getResourceId(R.styleable.SearchBar_android_textAppearance, -1); + String text = a.getString(R.styleable.SearchBar_android_text); + String hint = a.getString(R.styleable.SearchBar_android_hint); + float strokeWidth = a.getDimension(R.styleable.SearchBar_strokeWidth, -1); + int strokeColor = a.getColor(R.styleable.SearchBar_strokeColor, Color.TRANSPARENT); + + a.recycle(); + + if (!hideNavigationIcon) { + initNavigationIcon(); + } + setClickable(true); + setFocusable(true); + + LayoutInflater.from(context).inflate(R.layout.mtrl_search_bar, this); + layoutInflated = true; + + textView = findViewById(R.id.search_bar_text_view); + + ViewCompat.setElevation(this, elevation); + initTextView(textAppearanceResId, text, hint); + initBackground(shapeAppearanceModel, elevation, strokeWidth, strokeColor); + } + + private void validateAttributes(@Nullable AttributeSet attributeSet) { + if (attributeSet == null) { + return; + } + if (attributeSet.getAttributeValue(NAMESPACE_APP, "title") != null) { + throw new UnsupportedOperationException( + "SearchBar does not support title. Use hint or text instead."); + } + if (attributeSet.getAttributeValue(NAMESPACE_APP, "subtitle") != null) { + throw new UnsupportedOperationException( + "SearchBar does not support subtitle. Use hint or text instead."); + } + } + + private void initNavigationIcon() { + // If no navigation icon, set up the default one; otherwise, re-set it for tinting if needed. + setNavigationIcon(getNavigationIcon() == null ? defaultNavigationIcon : getNavigationIcon()); + + // Make the navigation icon button decorative (not clickable/focusable) by default so that the + // overall search bar handles the click. If a navigation icon click listener is set later on, + // the button will be made clickable/focusable. + setNavigationIconDecorative(true); + } + + private void initTextView(@StyleRes int textAppearanceResId, String text, String hint) { + if (textAppearanceResId != -1) { + TextViewCompat.setTextAppearance(textView, textAppearanceResId); + } + setText(text); + setHint(hint); + if (getNavigationIcon() == null) { + MarginLayoutParamsCompat.setMarginStart( + (MarginLayoutParams) textView.getLayoutParams(), + getResources() + .getDimensionPixelSize(R.dimen.m3_searchbar_text_margin_start_no_navigation_icon)); + } + } + + private void initBackground( + ShapeAppearanceModel shapeAppearance, + float elevation, + float strokeWidth, + @ColorInt int strokeColor) { + backgroundShape = new MaterialShapeDrawable(shapeAppearance); + backgroundShape.initializeElevationOverlay(getContext()); + backgroundShape.setElevation(elevation); + if (strokeWidth >= 0) { + backgroundShape.setStroke(strokeWidth, strokeColor); + } + + int backgroundColor = MaterialColors.getColor(this, R.attr.colorSurface); + int rippleColor = MaterialColors.getColor(this, R.attr.colorControlHighlight); + Drawable background; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + backgroundShape.setFillColor(ColorStateList.valueOf(backgroundColor)); + background = + new RippleDrawable(ColorStateList.valueOf(rippleColor), backgroundShape, backgroundShape); + } else { + backgroundShape.setFillColor(getCompatBackgroundColorStateList(backgroundColor, rippleColor)); + background = backgroundShape; + } + + ViewCompat.setBackground(this, background); + } + + private ColorStateList getCompatBackgroundColorStateList( + @ColorInt int backgroundColor, @ColorInt int rippleColor) { + int[][] states = + new int[][] { + new int[] {android.R.attr.state_pressed}, + new int[] {android.R.attr.state_focused}, + new int[] {}, + }; + int pressedBackgroundColor = MaterialColors.layer(backgroundColor, rippleColor); + int[] colors = new int[] {pressedBackgroundColor, pressedBackgroundColor, backgroundColor}; + return new ColorStateList(states, colors); + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (layoutInflated && centerView == null && !(child instanceof ActionMenuView)) { + centerView = child; + centerView.setAlpha(0); + } + super.addView(child, index, params); + } + + @RequiresApi(VERSION_CODES.LOLLIPOP) + @Override + public void setElevation(float elevation) { + super.setElevation(elevation); + if (backgroundShape != null) { + backgroundShape.setElevation(elevation); + } + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setClassName(EditText.class.getCanonicalName()); + CharSequence text = getText(); + boolean isTextEmpty = TextUtils.isEmpty(text); + if (VERSION.SDK_INT >= VERSION_CODES.O) { + info.setHintText(getHint()); + info.setShowingHintText(isTextEmpty); + } + + if (isTextEmpty) { + text = getHint(); + } + + info.setText(text); + } + + @Override + public void setNavigationOnClickListener(OnClickListener listener) { + if (forceDefaultNavigationOnClickListener) { + // Ignore the listener if forcing of the default navigation icon is enabled. + return; + } + super.setNavigationOnClickListener(listener); + setNavigationIconDecorative(listener == null); + } + + @Override + public void setNavigationIcon(@Nullable Drawable navigationIcon) { + super.setNavigationIcon(maybeTintNavigationIcon(navigationIcon)); + } + + @Nullable + private Drawable maybeTintNavigationIcon(@Nullable Drawable navigationIcon) { + if (!tintNavigationIcon || navigationIcon == null) { + return navigationIcon; + } + + int navigationIconColor; + if (navigationIconTint != null) { + navigationIconColor = navigationIconTint; + } else { + // Navigational icons such as the "hamburger", back arrow, etc. are supposed to be emphasized + // with colorOnSurface as opposed to colorOnSurfaceVariant. Here we assume any icon that's not + // the default search icon is navigational. + int navigationIconColorAttr = + navigationIcon == defaultNavigationIcon + ? R.attr.colorOnSurfaceVariant + : R.attr.colorOnSurface; + navigationIconColor = MaterialColors.getColor(this, navigationIconColorAttr); + } + + Drawable wrappedNavigationIcon = DrawableCompat.wrap(navigationIcon.mutate()); + DrawableCompat.setTint(wrappedNavigationIcon, navigationIconColor); + return wrappedNavigationIcon; + } + + private void setNavigationIconDecorative(boolean decorative) { + ImageButton navigationIconButton = ToolbarUtils.getNavigationIconButton(this); + if (navigationIconButton == null) { + return; + } + + navigationIconButton.setClickable(!decorative); + navigationIconButton.setFocusable(!decorative); + + Drawable navigationIconBackground = navigationIconButton.getBackground(); + if (navigationIconBackground != null) { + // Save original navigation icon background so we can restore it later if needed. + originalNavigationIconBackground = navigationIconBackground; + } + // Even if the navigation icon is not clickable/focusable, a ripple will still show up when the + // parent view (overall search bar) is clicked. So here we set the background to null to avoid + // that, and restore the original background when the icon becomes clickable. + navigationIconButton.setBackgroundDrawable( + decorative ? null : originalNavigationIconBackground); + } + + @Override + public void inflateMenu(@MenuRes int resId) { + super.inflateMenu(resId); + this.menuResId = resId; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + measureCenterView(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + layoutCenterView(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + MaterialShapeUtils.setParentAbsoluteElevation(this, backgroundShape); + setDefaultMargins(); + setOrClearDefaultScrollFlags(); + } + + /** + * {@link SearchBar} does not support the {@link Toolbar#setTitle} method, or its corresponding + * xml attribute. Instead, use {@link #setHint} or {@link #setText}, or their corresponding xml + * attributes, to provide a text affordance for your {@link SearchBar}. + */ + @Override + public void setTitle(CharSequence title) { + // Don't do anything. SearchBar can't have a title. + // Note: we can't throw an exception here because setTitle() is called by setSupportActionBar(). + } + + /** + * {@link SearchBar} does not support the {@link Toolbar#setSubtitle} method, or its corresponding + * xml attribute. Instead, use {@link #setHint} or {@link #setText}, or their corresponding xml + * attributes, to provide a text affordance for your {@link SearchBar}. + */ + @Override + public void setSubtitle(CharSequence subtitle) { + // Don't do anything. SearchBar can't have a subtitle. + // Note: we can't throw an exception here because setSubtitle() is called by + // ActionBar#setDisplayShowTitleEnabled(). + } + + private void setDefaultMargins() { + if (defaultMarginsEnabled && getLayoutParams() instanceof MarginLayoutParams) { + Resources resources = getResources(); + int marginHorizontal = + resources.getDimensionPixelSize(R.dimen.m3_searchbar_margin_horizontal); + int marginVertical = resources.getDimensionPixelSize(R.dimen.m3_searchbar_margin_vertical); + MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); + lp.leftMargin = defaultIfZero(lp.leftMargin, marginHorizontal); + lp.topMargin = defaultIfZero(lp.topMargin, marginVertical); + lp.rightMargin = defaultIfZero(lp.rightMargin, marginHorizontal); + lp.bottomMargin = defaultIfZero(lp.bottomMargin, marginVertical); + } + } + + private int defaultIfZero(int value, int defValue) { + return value == 0 ? defValue : value; + } + + private void setOrClearDefaultScrollFlags() { + if (getLayoutParams() instanceof AppBarLayout.LayoutParams) { + AppBarLayout.LayoutParams lp = (AppBarLayout.LayoutParams) getLayoutParams(); + if (defaultScrollFlagsEnabled) { + if (lp.getScrollFlags() == 0) { + lp.setScrollFlags(DEFAULT_SCROLL_FLAGS); + } + } else { + if (lp.getScrollFlags() == DEFAULT_SCROLL_FLAGS) { + lp.setScrollFlags(0); + } + } + } + } + + private void measureCenterView(int widthMeasureSpec, int heightMeasureSpec) { + if (centerView != null) { + centerView.measure(widthMeasureSpec, heightMeasureSpec); + } + } + + private void layoutCenterView() { + if (centerView == null) { + return; + } + + int centerViewWidth = centerView.getMeasuredWidth(); + int left = getMeasuredWidth() / 2 - centerViewWidth / 2; + int right = left + centerViewWidth; + + int centerViewHeight = centerView.getMeasuredHeight(); + int top = getMeasuredHeight() / 2 - centerViewHeight / 2; + int bottom = top + centerViewHeight; + + layoutChild(centerView, left, top, right, bottom); + } + + private void layoutChild(View child, int left, int top, int right, int bottom) { + if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL) { + child.layout(getMeasuredWidth() - right, top, getMeasuredWidth() - left, bottom); + } else { + child.layout(left, top, right, bottom); + } + } + + /** Returns the optional centered child view of this {@link SearchBar} */ + @Nullable + public View getCenterView() { + return centerView; + } + + /** + * Sets and adds the center view as a child. If you've set a center view and would like to remove + * it, pass in null for {@param view}. + */ + public void setCenterView(@Nullable View view) { + if (centerView != null) { + removeView(centerView); + centerView = null; + } + if (view != null) { + addView(view); + } + } + + /** Returns the main {@link TextView} which can be used for hint and search text. */ + @NonNull + public TextView getTextView() { + return textView; + } + + /** Returns the text of main {@link TextView}, which usually represents the search text. */ + @Nullable + public CharSequence getText() { + return textView.getText(); + } + + /** Sets the text of main {@link TextView}. */ + public void setText(@Nullable CharSequence text) { + textView.setText(text); + } + + /** Sets the text of main {@link TextView}. */ + public void setText(@StringRes int textResId) { + textView.setText(textResId); + } + + /** Clears the text of main {@link TextView}. */ + public void clearText() { + textView.setText(""); + } + + /** Returns the hint of main {@link TextView}. */ + @Nullable + public CharSequence getHint() { + return textView.getHint(); + } + + /** Sets the hint of main {@link TextView}. */ + public void setHint(@Nullable CharSequence hint) { + textView.setHint(hint); + } + + /** Sets the hint of main {@link TextView}. */ + public void setHint(@StringRes int hintResId) { + textView.setHint(hintResId); + } + + /** Returns the color of the {@link SearchBar} outline stroke. */ + @ColorInt + public int getStrokeColor() { + return backgroundShape.getStrokeColor().getDefaultColor(); + } + + /** Sets the color of the {@link SearchBar} outline stroke. */ + public void setStrokeColor(@ColorInt int strokeColor) { + if (getStrokeColor() != strokeColor) { + backgroundShape.setStrokeColor(ColorStateList.valueOf(strokeColor)); + } + } + + /** Returns the width in pixels of the {@link SearchBar} outline stroke. */ + @Dimension + public float getStrokeWidth() { + return backgroundShape.getStrokeWidth(); + } + + /** Sets the width in pixels of the {@link SearchBar} outline stroke. */ + public void setStrokeWidth(@Dimension float strokeWidth) { + if (getStrokeWidth() != strokeWidth) { + backgroundShape.setStrokeWidth(strokeWidth); + } + } + + /** Returns the size in pixels of the {@link SearchBar} corners. */ + public float getCornerSize() { + // Assume all corner sizes are the same. + return backgroundShape.getTopLeftCornerResolvedSize(); + } + + /** + * Returns whether the default {@link AppBarLayout} scroll flags are enabled. See {@link + * SearchBar#DEFAULT_SCROLL_FLAGS}. + */ + public boolean isDefaultScrollFlagsEnabled() { + return defaultScrollFlagsEnabled; + } + + /** + * Sets whether the default {@link AppBarLayout} scroll flags are enabled. See {@link + * SearchBar#DEFAULT_SCROLL_FLAGS}. + */ + public void setDefaultScrollFlagsEnabled(boolean defaultScrollFlagsEnabled) { + this.defaultScrollFlagsEnabled = defaultScrollFlagsEnabled; + setOrClearDefaultScrollFlags(); + } + + /** + * Starts the on load animation which transitions from the center view to the hint {@link + * TextView}. + */ + public void startOnLoadAnimation() { + // Use a post() to make sure the SearchBar's menu is inflated before the animation starts. + post(() -> searchBarAnimationHelper.startOnLoadAnimation(this)); + } + + /** + * Stops the on load animation which transitions from the center view to the hint {@link + * TextView}. + */ + public void stopOnLoadAnimation() { + searchBarAnimationHelper.stopOnLoadAnimation(this); + } + + /** Returns whether the fade in part is enabled for the on load animation. */ + public boolean isOnLoadAnimationFadeInEnabled() { + return searchBarAnimationHelper.isOnLoadAnimationFadeInEnabled(); + } + + /** Sets whether the fade in part is enabled for the on load animation. */ + public void setOnLoadAnimationFadeInEnabled(boolean onLoadAnimationFadeInEnabled) { + searchBarAnimationHelper.setOnLoadAnimationFadeInEnabled(onLoadAnimationFadeInEnabled); + } + + /** + * Registers a callback for the On Load Animation, started and stopped via {@link + * #startOnLoadAnimation()} and {@link #stopOnLoadAnimation()}. + */ + public void registerOnLoadAnimationCallback( + @NonNull OnLoadAnimationCallback onLoadAnimationCallback) { + searchBarAnimationHelper.registerOnLoadAnimationCallback(onLoadAnimationCallback); + } + + /** + * Unregisters a callback for the On Load Animation, started and stopped via {@link + * #startOnLoadAnimation()} and {@link #stopOnLoadAnimation()}. + */ + public boolean unregisterOnLoadAnimationCallback( + @NonNull OnLoadAnimationCallback onLoadAnimationCallback) { + return searchBarAnimationHelper.unregisterOnLoadAnimationCallback(onLoadAnimationCallback); + } + + /** + * Clears all registered callbacks for the On Load Animation, started and stopped via {@link + * #startOnLoadAnimation()} and {@link #stopOnLoadAnimation()}. + */ + public void clearOnLoadAnimationCallbacks() { + searchBarAnimationHelper.clearOnLoadAnimationCallbacks(); + } + + /** Returns whether the expand animation is running. */ + public boolean isExpanding() { + return searchBarAnimationHelper.isExpanding(); + } + + /** See {@link SearchBar#expand(View, AppBarLayout, boolean)}. */ + @CanIgnoreReturnValue + public boolean expand(@NonNull View expandedView) { + return expand(expandedView, /* appBarLayout= */ null); + } + + /** See {@link SearchBar#expand(View, AppBarLayout, boolean)}. */ + @CanIgnoreReturnValue + public boolean expand(@NonNull View expandedView, @Nullable AppBarLayout appBarLayout) { + return expand(expandedView, appBarLayout, /* skipAnimation= */ false); + } + + /** + * Starts an expand animation, if it's not already started, which transitions from the {@link + * SearchBar} to the {@code expandedView}, e.g., a contextual {@link Toolbar}. + * + *

Note: If you are using an {@link AppBarLayout} in conjunction with the {@link SearchBar}, + * you may pass in a reference to your {@link AppBarLayout} so that its visibility and offset can + * be taken into account for the animation. + * + * @return whether or not the expand animation was started + */ + @CanIgnoreReturnValue + public boolean expand( + @NonNull View expandedView, @Nullable AppBarLayout appBarLayout, boolean skipAnimation) { + // Start the expand if the expanded view is not already showing or in the process of expanding, + // or if the expanded view is collapsing since the final state should be expanded. + if ((expandedView.getVisibility() != View.VISIBLE && !isExpanding()) || isCollapsing()) { + searchBarAnimationHelper.startExpandAnimation( + this, expandedView, appBarLayout, skipAnimation); + return true; + } + return false; + } + + /** + * Adds a listener for the expand animation started via {@link #expand(View)} and {@link + * #expand(View, AppBarLayout)}. + */ + public void addExpandAnimationListener(@NonNull AnimatorListenerAdapter listener) { + searchBarAnimationHelper.addExpandAnimationListener(listener); + } + + /** + * Removes a listener for the expand animation started via {@link #expand(View)} and {@link + * #expand(View, AppBarLayout)}. + * + * @return true if a listener was removed as a result of this call + */ + public boolean removeExpandAnimationListener(@NonNull AnimatorListenerAdapter listener) { + return searchBarAnimationHelper.removeExpandAnimationListener(listener); + } + + /** + * Removes all expand animation listeners added via {@link + * #addExpandAnimationListener(AnimatorListenerAdapter)}. + */ + public void clearExpandAnimationListeners() { + searchBarAnimationHelper.clearExpandAnimationListeners(); + } + + /** Returns whether the collapse animation is running. */ + public boolean isCollapsing() { + return searchBarAnimationHelper.isCollapsing(); + } + + /** See {@link SearchBar#collapse(View, AppBarLayout, boolean)}. */ + @CanIgnoreReturnValue + public boolean collapse(@NonNull View expandedView) { + return collapse(expandedView, /* appBarLayout= */ null); + } + + /** See {@link SearchBar#collapse(View, AppBarLayout, boolean)}. */ + @CanIgnoreReturnValue + public boolean collapse(@NonNull View expandedView, @Nullable AppBarLayout appBarLayout) { + return collapse(expandedView, /* appBarLayout= */ appBarLayout, /* skipAnimation= */ false); + } + + /** + * Starts a collapse animation, if it's not already started, which transitions from the {@code + * expandedView}, e.g., a contextual {@link Toolbar}, to the {@link SearchBar}. + * + *

Note: If you are using an {@link AppBarLayout} in conjunction with the {@link SearchBar}, + * you may pass in a reference to your {@link AppBarLayout} so that its visibility and offset can + * be taken into account for the animation. + * + * @return whether or not the collapse animation was started + */ + @CanIgnoreReturnValue + public boolean collapse( + @NonNull View expandedView, @Nullable AppBarLayout appBarLayout, boolean skipAnimation) { + // Start the collapse if the expanded view is showing and not in the process of collapsing, or + // if the expanded view is expanding since the final state should be collapsed. + if ((expandedView.getVisibility() == View.VISIBLE && !isCollapsing()) || isExpanding()) { + searchBarAnimationHelper.startCollapseAnimation( + this, expandedView, appBarLayout, skipAnimation); + return true; + } + return false; + } + + /** + * Adds a listener for the collapse animation started via {@link #collapse(View)} and {@link + * #collapse(View, AppBarLayout)}. + */ + public void addCollapseAnimationListener(@NonNull AnimatorListenerAdapter listener) { + searchBarAnimationHelper.addCollapseAnimationListener(listener); + } + + /** + * Removes a listener for the collapse animation started via {@link #collapse(View)} and {@link + * #collapse(View, AppBarLayout)}. + * + * @return true if a listener was removed as a result of this call + */ + public boolean removeCollapseAnimationListener(@NonNull AnimatorListenerAdapter listener) { + return searchBarAnimationHelper.removeCollapseAnimationListener(listener); + } + + /** + * Removes all collapse animation listeners added via {@link + * #addCollapseAnimationListener(AnimatorListenerAdapter)}. + */ + public void clearCollapseAnimationListeners() { + searchBarAnimationHelper.clearCollapseAnimationListeners(); + } + + int getMenuResId() { + return menuResId; + } + + float getCompatElevation() { + return backgroundShape != null ? backgroundShape.getElevation() : ViewCompat.getElevation(this); + } + + /** Behavior that sets up the scroll-away mode for an {@link SearchBar}. */ + public static class ScrollingViewBehavior extends AppBarLayout.ScrollingViewBehavior { + + private boolean initialized = false; + + public ScrollingViewBehavior() {} + + public ScrollingViewBehavior(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + @Override + public boolean onDependentViewChanged( + @NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) { + boolean changed = super.onDependentViewChanged(parent, child, dependency); + if (!initialized && dependency instanceof AppBarLayout) { + initialized = true; + AppBarLayout appBarLayout = (AppBarLayout) dependency; + setAppBarLayoutTransparent(appBarLayout); + } + return changed; + } + + private void setAppBarLayoutTransparent(AppBarLayout appBarLayout) { + appBarLayout.setBackgroundColor(Color.TRANSPARENT); + + // Remove AppBarLayout elevation shadow + if (Build.VERSION.SDK_INT == VERSION_CODES.LOLLIPOP) { + // Workaround for elevation crash that only happens on Android 5.0 + // Similar to https://stackoverflow.com/q/40928788 + appBarLayout.setOutlineProvider(null); + } else { + appBarLayout.setTargetElevation(0); + } + } + + @Override + protected boolean shouldHeaderOverlapScrollingChild() { + return true; + } + } + + /** + * Callback for the animation started and stopped via {@link #startOnLoadAnimation()} and {@link + * #stopOnLoadAnimation()}. + */ + public abstract static class OnLoadAnimationCallback { + public void onAnimationStart() {} + + public void onAnimationEnd() {} + } + + @Override + @NonNull + protected Parcelable onSaveInstanceState() { + SavedState savedState = new SavedState(super.onSaveInstanceState()); + CharSequence text = getText(); + savedState.text = text == null ? null : text.toString(); + return savedState; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (!(state instanceof SavedState)) { + super.onRestoreInstanceState(state); + return; + } + SavedState savedState = (SavedState) state; + super.onRestoreInstanceState(savedState.getSuperState()); + setText(savedState.text); + } + + static class SavedState extends AbsSavedState { + + String text; + + public SavedState(Parcel source) { + this(source, null); + } + + public SavedState(Parcel source, @Nullable ClassLoader classLoader) { + super(source, classLoader); + text = source.readString(); + } + + public SavedState(Parcelable superState) { + super(superState); + } + + public static final Parcelable.Creator CREATOR = + new ClassLoaderCreator() { + + @Override + public SavedState createFromParcel(Parcel source, ClassLoader loader) { + return new SavedState(source, loader); + } + + @Override + public SavedState createFromParcel(Parcel source) { + return new SavedState(source); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(text); + } + } +} diff --git a/lib/java/com/google/android/material/search/SearchBarAnimationHelper.java b/lib/java/com/google/android/material/search/SearchBarAnimationHelper.java new file mode 100644 index 00000000000..5e1a4f1ba46 --- /dev/null +++ b/lib/java/com/google/android/material/search/SearchBarAnimationHelper.java @@ -0,0 +1,423 @@ +/* + * Copyright 2022 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 + * + * https://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.google.android.material.search; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import androidx.appcompat.widget.ActionMenuView; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.ViewCompat; +import com.google.android.material.animation.AnimatableView; +import com.google.android.material.animation.AnimationUtils; +import com.google.android.material.appbar.AppBarLayout; +import com.google.android.material.internal.ExpandCollapseAnimationHelper; +import com.google.android.material.internal.MultiViewUpdateListener; +import com.google.android.material.internal.ToolbarUtils; +import com.google.android.material.internal.ViewUtils; +import com.google.android.material.search.SearchBar.OnLoadAnimationCallback; +import com.google.android.material.shape.MaterialShapeDrawable; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** Helper class for {@link SearchBar} animations. */ +class SearchBarAnimationHelper { + + // On load animation constants + private static final long ON_LOAD_ANIM_CENTER_VIEW_DEFAULT_FADE_DURATION_MS = 250; + private static final long ON_LOAD_ANIM_CENTER_VIEW_DEFAULT_FADE_IN_START_DELAY_MS = 500; + private static final long ON_LOAD_ANIM_CENTER_VIEW_DEFAULT_FADE_OUT_START_DELAY_MS = 750; + private static final long ON_LOAD_ANIM_SECONDARY_DURATION_MS = 250; + private static final long ON_LOAD_ANIM_SECONDARY_START_DELAY_MS = 250; + + // Expand and collapse animation constants + private static final long EXPAND_DURATION_MS = 300; + private static final long EXPAND_FADE_OUT_CHILDREN_DURATION_MS = 75; + private static final long COLLAPSE_DURATION_MS = 250; + private static final long COLLAPSE_FADE_IN_CHILDREN_DURATION_MS = 100; + + private final Set onLoadAnimationCallbacks = new LinkedHashSet<>(); + private final Set expandAnimationListeners = new LinkedHashSet<>(); + private final Set collapseAnimationListeners = new LinkedHashSet<>(); + + @Nullable private Animator secondaryViewAnimator; + @Nullable private Animator defaultCenterViewAnimator; + private boolean expanding; + private boolean collapsing; + private boolean onLoadAnimationFadeInEnabled = true; + private Animator runningExpandOrCollapseAnimator = null; + + void startOnLoadAnimation(SearchBar searchBar) { + dispatchOnLoadAnimation(OnLoadAnimationCallback::onAnimationStart); + TextView textView = searchBar.getTextView(); + View centerView = searchBar.getCenterView(); + + View secondaryActionMenuItemView = ToolbarUtils.getSecondaryActionMenuItemView(searchBar); + Animator secondaryViewAnimator = + getSecondaryViewAnimator(textView, secondaryActionMenuItemView); + secondaryViewAnimator.addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + dispatchOnLoadAnimation(OnLoadAnimationCallback::onAnimationEnd); + } + }); + this.secondaryViewAnimator = secondaryViewAnimator; + + textView.setAlpha(0f); + if (secondaryActionMenuItemView != null) { + secondaryActionMenuItemView.setAlpha(0f); + } + if (centerView instanceof AnimatableView) { + ((AnimatableView) centerView).startAnimation(secondaryViewAnimator::start); + } else if (centerView != null) { + centerView.setAlpha(0f); + centerView.setVisibility(View.VISIBLE); + Animator defaultCenterViewAnimator = getDefaultCenterViewAnimator(centerView); + this.defaultCenterViewAnimator = defaultCenterViewAnimator; + defaultCenterViewAnimator.addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + centerView.setVisibility(View.GONE); + secondaryViewAnimator.start(); + } + }); + defaultCenterViewAnimator.start(); + } else { + secondaryViewAnimator.start(); + } + } + + void stopOnLoadAnimation(SearchBar searchBar) { + if (secondaryViewAnimator != null) { + secondaryViewAnimator.end(); + } + if (defaultCenterViewAnimator != null) { + defaultCenterViewAnimator.end(); + } + View centerView = searchBar.getCenterView(); + if (centerView instanceof AnimatableView) { + ((AnimatableView) centerView).stopAnimation(); + } + if (centerView != null) { + centerView.setAlpha(0); + } + } + + boolean isOnLoadAnimationFadeInEnabled() { + return onLoadAnimationFadeInEnabled; + } + + void setOnLoadAnimationFadeInEnabled(boolean onLoadAnimationFadeInEnabled) { + this.onLoadAnimationFadeInEnabled = onLoadAnimationFadeInEnabled; + } + + void registerOnLoadAnimationCallback(OnLoadAnimationCallback onLoadAnimationCallback) { + onLoadAnimationCallbacks.add(onLoadAnimationCallback); + } + + boolean unregisterOnLoadAnimationCallback(OnLoadAnimationCallback onLoadAnimationCallback) { + return onLoadAnimationCallbacks.remove(onLoadAnimationCallback); + } + + void clearOnLoadAnimationCallbacks() { + onLoadAnimationCallbacks.clear(); + } + + private void dispatchOnLoadAnimation(OnLoadAnimationInvocation invocation) { + for (OnLoadAnimationCallback onLoadAnimationCallback : onLoadAnimationCallbacks) { + invocation.invoke(onLoadAnimationCallback); + } + } + + private interface OnLoadAnimationInvocation { + void invoke(OnLoadAnimationCallback onLoadAnimationCallback); + } + + private Animator getDefaultCenterViewAnimator(@Nullable View centerView) { + ValueAnimator fadeInAnimator = ValueAnimator.ofFloat(0, 1); + fadeInAnimator.addUpdateListener(MultiViewUpdateListener.alphaListener(centerView)); + fadeInAnimator.setInterpolator(AnimationUtils.LINEAR_INTERPOLATOR); + fadeInAnimator.setDuration( + onLoadAnimationFadeInEnabled ? ON_LOAD_ANIM_CENTER_VIEW_DEFAULT_FADE_DURATION_MS : 0); + fadeInAnimator.setStartDelay( + onLoadAnimationFadeInEnabled ? ON_LOAD_ANIM_CENTER_VIEW_DEFAULT_FADE_IN_START_DELAY_MS : 0); + + ValueAnimator fadeOutAnimator = ValueAnimator.ofFloat(1, 0); + fadeOutAnimator.addUpdateListener(MultiViewUpdateListener.alphaListener(centerView)); + fadeOutAnimator.setInterpolator(AnimationUtils.LINEAR_INTERPOLATOR); + fadeOutAnimator.setDuration(ON_LOAD_ANIM_CENTER_VIEW_DEFAULT_FADE_DURATION_MS); + fadeOutAnimator.setStartDelay(ON_LOAD_ANIM_CENTER_VIEW_DEFAULT_FADE_OUT_START_DELAY_MS); + + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.playSequentially(fadeInAnimator, fadeOutAnimator); + return animatorSet; + } + + private Animator getSecondaryViewAnimator( + TextView textView, @Nullable View secondaryActionMenuItemView) { + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.setStartDelay(ON_LOAD_ANIM_SECONDARY_START_DELAY_MS); + animatorSet.play(getTextViewAnimator(textView)); + if (secondaryActionMenuItemView != null) { + animatorSet.play(getSecondaryActionMenuItemAnimator(secondaryActionMenuItemView)); + } + return animatorSet; + } + + private Animator getTextViewAnimator(TextView textView) { + ValueAnimator animator = ValueAnimator.ofFloat(0, 1); + animator.addUpdateListener(MultiViewUpdateListener.alphaListener(textView)); + animator.setInterpolator(AnimationUtils.LINEAR_INTERPOLATOR); + animator.setDuration(ON_LOAD_ANIM_SECONDARY_DURATION_MS); + return animator; + } + + private Animator getSecondaryActionMenuItemAnimator(@Nullable View secondaryActionMenuItemView) { + ValueAnimator animator = ValueAnimator.ofFloat(0, 1); + animator.addUpdateListener(MultiViewUpdateListener.alphaListener(secondaryActionMenuItemView)); + animator.setInterpolator(AnimationUtils.LINEAR_INTERPOLATOR); + animator.setDuration(ON_LOAD_ANIM_SECONDARY_DURATION_MS); + return animator; + } + + void startExpandAnimation( + SearchBar searchBar, + View expandedView, + @Nullable AppBarLayout appBarLayout, + boolean skipAnimation) { + // If we are in the middle of an collapse animation we should cancel it before we start the + // expand. + if (isCollapsing() && runningExpandOrCollapseAnimator != null) { + runningExpandOrCollapseAnimator.cancel(); + } + expanding = true; + expandedView.setVisibility(View.INVISIBLE); + expandedView.post( + () -> { + AnimatorSet fadeAndExpandAnimatorSet = new AnimatorSet(); + Animator fadeOutChildrenAnimator = getFadeOutChildrenAnimator(searchBar, expandedView); + Animator expandAnimator = getExpandAnimator(searchBar, expandedView, appBarLayout); + + fadeAndExpandAnimatorSet.playSequentially(fadeOutChildrenAnimator, expandAnimator); + fadeAndExpandAnimatorSet.addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + runningExpandOrCollapseAnimator = null; + } + }); + for (AnimatorListenerAdapter listener : expandAnimationListeners) { + fadeAndExpandAnimatorSet.addListener(listener); + } + if (skipAnimation) { + fadeAndExpandAnimatorSet.setDuration(0); + } + fadeAndExpandAnimatorSet.start(); + + runningExpandOrCollapseAnimator = fadeAndExpandAnimatorSet; + }); + } + + private Animator getExpandAnimator( + SearchBar searchBar, View expandedView, @Nullable AppBarLayout appBarLayout) { + return getExpandCollapseAnimationHelper(searchBar, expandedView, appBarLayout) + .setDuration(EXPAND_DURATION_MS) + .addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + searchBar.setVisibility(View.INVISIBLE); + } + + @Override + public void onAnimationEnd(Animator animation) { + expanding = false; + } + }) + .getExpandAnimator(); + } + + boolean isExpanding() { + return expanding; + } + + void addExpandAnimationListener(@NonNull AnimatorListenerAdapter listener) { + expandAnimationListeners.add(listener); + } + + boolean removeExpandAnimationListener(@NonNull AnimatorListenerAdapter listener) { + return expandAnimationListeners.remove(listener); + } + + void clearExpandAnimationListeners() { + expandAnimationListeners.clear(); + } + + void startCollapseAnimation( + SearchBar searchBar, + View expandedView, + @Nullable AppBarLayout appBarLayout, + boolean skipAnimation) { + // If we are in the middle of an expand animation we should cancel it before we start the + // collapse. + if (isExpanding() && runningExpandOrCollapseAnimator != null) { + runningExpandOrCollapseAnimator.cancel(); + } + + collapsing = true; + AnimatorSet collapseAndFadeAnimatorSet = new AnimatorSet(); + Animator collapseAnimator = getCollapseAnimator(searchBar, expandedView, appBarLayout); + Animator fadeInChildrenAnimator = getFadeInChildrenAnimator(searchBar); + + collapseAndFadeAnimatorSet.playSequentially(collapseAnimator, fadeInChildrenAnimator); + collapseAndFadeAnimatorSet.addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + runningExpandOrCollapseAnimator = null; + } + }); + for (AnimatorListenerAdapter listener : collapseAnimationListeners) { + collapseAndFadeAnimatorSet.addListener(listener); + } + if (skipAnimation) { + collapseAndFadeAnimatorSet.setDuration(0); + } + collapseAndFadeAnimatorSet.start(); + + runningExpandOrCollapseAnimator = collapseAndFadeAnimatorSet; + } + + private Animator getCollapseAnimator( + SearchBar searchBar, View expandedView, AppBarLayout appBarLayout) { + return getExpandCollapseAnimationHelper(searchBar, expandedView, appBarLayout) + .setDuration(COLLAPSE_DURATION_MS) + .addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + searchBar.stopOnLoadAnimation(); + } + + @Override + public void onAnimationEnd(Animator animation) { + searchBar.setVisibility(View.VISIBLE); + collapsing = false; + } + }) + .getCollapseAnimator(); + } + + boolean isCollapsing() { + return collapsing; + } + + void addCollapseAnimationListener(@NonNull AnimatorListenerAdapter listener) { + collapseAnimationListeners.add(listener); + } + + boolean removeCollapseAnimationListener(@NonNull AnimatorListenerAdapter listener) { + return collapseAnimationListeners.remove(listener); + } + + void clearCollapseAnimationListeners() { + collapseAnimationListeners.clear(); + } + + private ExpandCollapseAnimationHelper getExpandCollapseAnimationHelper( + SearchBar searchBar, View expandedView, @Nullable AppBarLayout appBarLayout) { + return new ExpandCollapseAnimationHelper(searchBar, expandedView) + .setAdditionalUpdateListener( + getExpandedViewBackgroundUpdateListener(searchBar, expandedView)) + .setCollapsedViewOffsetY(appBarLayout != null ? appBarLayout.getTop() : 0) + .addEndAnchoredViews(getEndAnchoredViews(expandedView)); + } + + private AnimatorUpdateListener getExpandedViewBackgroundUpdateListener( + SearchBar searchBar, View expandedView) { + MaterialShapeDrawable expandedViewBackground = + MaterialShapeDrawable.createWithElevationOverlay(expandedView.getContext()); + expandedViewBackground.setCornerSize(searchBar.getCornerSize()); + expandedViewBackground.setElevation(ViewCompat.getElevation(searchBar)); + + return valueAnimator -> { + expandedViewBackground.setInterpolation(1 - valueAnimator.getAnimatedFraction()); + ViewCompat.setBackground(expandedView, expandedViewBackground); + + // Ensures that the expanded view is visible, in the case where ActionMode is used. + expandedView.setAlpha(1); + }; + } + + private Animator getFadeOutChildrenAnimator(SearchBar searchBar, View expandedView) { + List children = getFadeChildren(searchBar); + ValueAnimator animator = ValueAnimator.ofFloat(1, 0); + animator.addUpdateListener(MultiViewUpdateListener.alphaListener(children)); + animator.addUpdateListener( + animation -> { + // Ensures that the expanded view is not visible while the children are fading out, in + // the case where ActionMode is used. + expandedView.setAlpha(0); + }); + animator.setDuration(EXPAND_FADE_OUT_CHILDREN_DURATION_MS); + animator.setInterpolator(AnimationUtils.LINEAR_INTERPOLATOR); + return animator; + } + + private Animator getFadeInChildrenAnimator(SearchBar searchBar) { + List children = getFadeChildren(searchBar); + ValueAnimator animator = ValueAnimator.ofFloat(0, 1); + animator.addUpdateListener(MultiViewUpdateListener.alphaListener(children)); + animator.setDuration(COLLAPSE_FADE_IN_CHILDREN_DURATION_MS); + animator.setInterpolator(AnimationUtils.LINEAR_INTERPOLATOR); + return animator; + } + + private List getFadeChildren(SearchBar searchBar) { + List children = ViewUtils.getChildren(searchBar); + if (searchBar.getCenterView() != null) { + children.remove(searchBar.getCenterView()); + } + return children; + } + + private List getEndAnchoredViews(View expandedView) { + boolean isRtl = ViewUtils.isLayoutRtl(expandedView); + List endAnchoredViews = new ArrayList<>(); + if (expandedView instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) expandedView; + for (int i = 0; i < viewGroup.getChildCount(); i++) { + View child = viewGroup.getChildAt(i); + if ((!isRtl && child instanceof ActionMenuView) + || (isRtl && !(child instanceof ActionMenuView))) { + endAnchoredViews.add(child); + } + } + } + return endAnchoredViews; + } +} diff --git a/lib/java/com/google/android/material/search/SearchView.java b/lib/java/com/google/android/material/search/SearchView.java new file mode 100644 index 00000000000..a980ce6d61e --- /dev/null +++ b/lib/java/com/google/android/material/search/SearchView.java @@ -0,0 +1,942 @@ +/* + * Copyright 2022 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 + * + * https://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.google.android.material.search; + +import com.google.android.material.R; + +import static com.google.android.material.theme.overlay.MaterialThemeOverlay.wrap; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.os.Parcel; +import android.os.Parcelable; +import androidx.appcompat.content.res.AppCompatResources; +import androidx.appcompat.graphics.drawable.DrawerArrowDrawable; +import androidx.appcompat.widget.Toolbar; +import androidx.appcompat.widget.Toolbar.OnMenuItemClickListener; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.EditText; +import android.widget.FrameLayout; +import android.widget.ImageButton; +import android.widget.TextView; +import androidx.annotation.MenuRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.Px; +import androidx.annotation.RequiresApi; +import androidx.annotation.StringRes; +import androidx.annotation.StyleRes; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.core.graphics.ColorUtils; +import androidx.core.graphics.drawable.DrawableCompat; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; +import androidx.core.widget.TextViewCompat; +import androidx.customview.view.AbsSavedState; +import com.google.android.material.appbar.MaterialToolbar; +import com.google.android.material.color.MaterialColors; +import com.google.android.material.elevation.ElevationOverlayProvider; +import com.google.android.material.internal.ClippableRoundedCornerLayout; +import com.google.android.material.internal.ContextUtils; +import com.google.android.material.internal.FadeThroughDrawable; +import com.google.android.material.internal.ThemeEnforcement; +import com.google.android.material.internal.ToolbarUtils; +import com.google.android.material.internal.TouchObserverFrameLayout; +import com.google.android.material.internal.ViewUtils; +import com.google.android.material.shape.MaterialShapeUtils; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +/** Layout that provides a full screen search view and can be used with {@link SearchBar}. */ +@SuppressWarnings("RestrictTo") +public class SearchView extends FrameLayout implements CoordinatorLayout.AttachedBehavior { + + private static final int DEF_STYLE_RES = R.style.Widget_Material3_SearchView; + + final View scrim; + final ClippableRoundedCornerLayout rootView; + final View backgroundView; + final View statusBarSpacer; + final FrameLayout headerContainer; + final FrameLayout toolbarContainer; + final MaterialToolbar toolbar; + final Toolbar dummyToolbar; + final TextView searchPrefix; + final EditText editText; + final ImageButton clearButton; + final View divider; + final TouchObserverFrameLayout contentContainer; + + private final boolean layoutInflated; + private final SearchViewAnimationHelper searchViewAnimationHelper; + private final ElevationOverlayProvider elevationOverlayProvider; + private final Set transitionListeners = new LinkedHashSet<>(); + + @Nullable private SearchBar searchBar; + private int softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; + private boolean animatedNavigationIcon; + private boolean animatedMenuItems; + private boolean autoShowKeyboard; + private boolean useWindowInsetsController; + @NonNull private TransitionState currentTransitionState = TransitionState.HIDDEN; + private Map childImportantForAccessibilityMap; + + public SearchView(@NonNull Context context) { + this(context, null); + } + + public SearchView(@NonNull Context context, @Nullable AttributeSet attrs) { + this(context, attrs, R.attr.searchViewStyle); + } + + public SearchView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(wrap(context, attrs, defStyleAttr, DEF_STYLE_RES), attrs, defStyleAttr); + // Ensure we are using the correctly themed context rather than the context that was passed in. + context = getContext(); + + TypedArray a = + ThemeEnforcement.obtainStyledAttributes( + context, attrs, R.styleable.SearchView, defStyleAttr, DEF_STYLE_RES); + + int headerLayoutResId = a.getResourceId(R.styleable.SearchView_headerLayout, -1); + int textAppearanceResId = a.getResourceId(R.styleable.SearchView_android_textAppearance, -1); + String text = a.getString(R.styleable.SearchView_android_text); + String hint = a.getString(R.styleable.SearchView_android_hint); + String searchPrefixText = a.getString(R.styleable.SearchView_searchPrefixText); + boolean useDrawerArrowDrawable = + a.getBoolean(R.styleable.SearchView_useDrawerArrowDrawable, false); + animatedNavigationIcon = a.getBoolean(R.styleable.SearchView_animateNavigationIcon, true); + animatedMenuItems = a.getBoolean(R.styleable.SearchView_animateMenuItems, true); + boolean hideNavigationIcon = a.getBoolean(R.styleable.SearchView_hideNavigationIcon, false); + autoShowKeyboard = a.getBoolean(R.styleable.SearchView_autoShowKeyboard, true); + + a.recycle(); + + LayoutInflater.from(context).inflate(R.layout.mtrl_search_view, this); + layoutInflated = true; + + scrim = findViewById(R.id.search_view_scrim); + rootView = findViewById(R.id.search_view_root); + backgroundView = findViewById(R.id.search_view_background); + statusBarSpacer = findViewById(R.id.search_view_status_bar_spacer); + headerContainer = findViewById(R.id.search_view_header_container); + toolbarContainer = findViewById(R.id.search_view_toolbar_container); + toolbar = findViewById(R.id.search_view_toolbar); + dummyToolbar = findViewById(R.id.search_view_dummy_toolbar); + searchPrefix = findViewById(R.id.search_view_search_prefix); + editText = findViewById(R.id.search_view_edit_text); + clearButton = findViewById(R.id.search_view_clear_button); + divider = findViewById(R.id.search_view_divider); + contentContainer = findViewById(R.id.search_view_content_container); + + searchViewAnimationHelper = new SearchViewAnimationHelper(this); + elevationOverlayProvider = new ElevationOverlayProvider(context); + + setUpRootView(); + setUpBackgroundViewElevationOverlay(); + setUpHeaderLayout(headerLayoutResId); + setSearchPrefixText(searchPrefixText); + setUpEditText(textAppearanceResId, text, hint); + setUpBackButton(useDrawerArrowDrawable, hideNavigationIcon); + setUpClearButton(); + setUpDivider(); + setUpContentOnTouchListener(); + setUpInsetListeners(); + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (layoutInflated) { + contentContainer.addView(child, index, params); + } else { + super.addView(child, index, params); + } + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + Activity activity = ContextUtils.getActivity(getContext()); + if (activity != null) { + Window window = activity.getWindow(); + setSoftInputMode(window); + setStatusBarSpacerEnabled(shouldShowStatusBarSpacer(window)); + } + } + + @RequiresApi(VERSION_CODES.LOLLIPOP) + @Override + public void setElevation(float elevation) { + super.setElevation(elevation); + setUpBackgroundViewElevationOverlay(elevation); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + MaterialShapeUtils.setParentAbsoluteElevation(this); + } + + @Override + @NonNull + public CoordinatorLayout.Behavior getBehavior() { + return new SearchView.Behavior(); + } + + @SuppressLint("ClickableViewAccessibility") // Will be handled by accessibility delegate. + private void setUpRootView() { + rootView.setOnTouchListener((v, event) -> true); + } + + private void setUpBackgroundViewElevationOverlay() { + setUpBackgroundViewElevationOverlay(getOverlayElevation()); + } + + private void setUpBackgroundViewElevationOverlay(float elevation) { + if (elevationOverlayProvider == null || backgroundView == null) { + return; + } + int backgroundColor = + elevationOverlayProvider.compositeOverlayWithThemeSurfaceColorIfNeeded(elevation); + backgroundView.setBackgroundColor(backgroundColor); + } + + private float getOverlayElevation() { + if (searchBar != null) { + return searchBar.getCompatElevation(); + } else { + return getResources().getDimension(R.dimen.m3_searchview_elevation); + } + } + + private void setUpHeaderLayout(int headerLayoutResId) { + if (headerLayoutResId != -1) { + View headerView = + LayoutInflater.from(getContext()).inflate(headerLayoutResId, headerContainer, false); + addHeaderView(headerView); + } + } + + private void setUpEditText(@StyleRes int textAppearanceResId, String text, String hint) { + if (textAppearanceResId != -1) { + TextViewCompat.setTextAppearance(editText, textAppearanceResId); + } + editText.setText(text); + editText.setHint(hint); + } + + private void setUpBackButton(boolean useDrawerArrowDrawable, boolean hideNavigationIcon) { + if (hideNavigationIcon) { + toolbar.setNavigationIcon(null); + return; + } + + toolbar.setNavigationOnClickListener(v -> hide()); + + if (useDrawerArrowDrawable) { + DrawerArrowDrawable drawerArrowDrawable = new DrawerArrowDrawable(getContext()); + drawerArrowDrawable.setColor(MaterialColors.getColor(this, R.attr.colorOnSurface)); + toolbar.setNavigationIcon(drawerArrowDrawable); + } + } + + private void setUpClearButton() { + clearButton.setOnClickListener( + v -> { + clearText(); + requestFocusAndShowKeyboardIfNeeded(); + }); + + editText.addTextChangedListener( + new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + clearButton.setVisibility(s.length() > 0 ? VISIBLE : GONE); + } + + @Override + public void afterTextChanged(Editable s) {} + }); + } + + private void setUpDivider() { + int colorOnSurface = MaterialColors.getColor(this, R.attr.colorOnSurface); + int dividerColor = ColorUtils.setAlphaComponent(colorOnSurface, Math.round(0.12f * 255)); + divider.setBackgroundColor(dividerColor); + } + + @SuppressLint("ClickableViewAccessibility") // Will be handled by accessibility delegate. + private void setUpContentOnTouchListener() { + contentContainer.setOnTouchListener( + (v, event) -> { + if (isAdjustNothingSoftInputMode()) { + clearFocusAndHideKeyboard(); + } + return false; + }); + } + + private void setUpStatusBarSpacer(@Px int height) { + if (statusBarSpacer.getLayoutParams().height != height) { + statusBarSpacer.getLayoutParams().height = height; + statusBarSpacer.requestLayout(); + } + } + + @Px + private int getStatusBarHeight() { + @SuppressLint({ + "DiscouragedApi", + "InternalInsetResource" + }) // Used for initial value. A WindowInsetsListener will apply correct insets later. + int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + return getResources().getDimensionPixelSize(resourceId); + } else { + return 0; + } + } + + /** + * Note: DrawerArrowDrawable supports RTL, so there is no need to update the navigation icon for + * RTL if it is a DrawerArrowDrawable. + */ + private void updateNavigationIconIfNeeded() { + if (toolbar == null) { + return; + } + + if (isNavigationIconDrawerArrowDrawable(toolbar)) { + return; + } + + int navigationIcon = R.drawable.ic_arrow_back_black_24; + if (searchBar == null) { + toolbar.setNavigationIcon(navigationIcon); + } else { + Drawable navigationIconDrawable = + DrawableCompat.wrap( + AppCompatResources.getDrawable(getContext(), navigationIcon).mutate()); + if (toolbar.getNavigationIconTint() != null) { + DrawableCompat.setTint(navigationIconDrawable, toolbar.getNavigationIconTint()); + } + toolbar.setNavigationIcon( + new FadeThroughDrawable(searchBar.getNavigationIcon(), navigationIconDrawable)); + updateNavigationIconProgressIfNeeded(); + } + } + + private boolean isNavigationIconDrawerArrowDrawable(@NonNull Toolbar toolbar) { + return DrawableCompat.unwrap(toolbar.getNavigationIcon()) instanceof DrawerArrowDrawable; + } + + /** + * Listens to {@link WindowInsetsCompat} and adjusts layouts accordingly. + * + *

NOTE: window insets are only delivered if no other layout consumed them before. E.g.: + * + *

+ */ + private void setUpInsetListeners() { + setUpToolbarInsetListener(); + setUpDividerInsetListener(); + setUpStatusBarSpacerInsetListener(); + } + + private void setUpToolbarInsetListener() { + ViewUtils.doOnApplyWindowInsets( + toolbar, + (view, insets, initialPadding) -> { + boolean isRtl = ViewUtils.isLayoutRtl(toolbar); + int paddingLeft = isRtl ? initialPadding.end : initialPadding.start; + int paddingRight = isRtl ? initialPadding.start : initialPadding.end; + toolbar.setPadding( + paddingLeft + insets.getSystemWindowInsetLeft(), initialPadding.top, + paddingRight + insets.getSystemWindowInsetRight(), initialPadding.bottom); + return insets; + }); + } + + private void setUpStatusBarSpacerInsetListener() { + // Set an initial height based on the default system value to support pre-L behavior. + setUpStatusBarSpacer(getStatusBarHeight()); + + // Listen to system window insets on L+ and adjusts status bar height based on the top inset. + ViewCompat.setOnApplyWindowInsetsListener( + statusBarSpacer, + (v, insets) -> { + setUpStatusBarSpacer(insets.getSystemWindowInsetTop()); + return insets; + }); + } + + private void setUpDividerInsetListener() { + MarginLayoutParams layoutParams = (MarginLayoutParams) divider.getLayoutParams(); + int leftMargin = layoutParams.leftMargin; + int rightMargin = layoutParams.rightMargin; + ViewCompat.setOnApplyWindowInsetsListener( + divider, + (v, insets) -> { + layoutParams.leftMargin = leftMargin + insets.getSystemWindowInsetLeft(); + layoutParams.rightMargin = rightMargin + insets.getSystemWindowInsetRight(); + return insets; + }); + } + + /** Returns whether or not this {@link SearchView} is set up with an {@link SearchBar}. */ + public boolean isSetUpWithSearchBar() { + return this.searchBar != null; + } + + /** + * Sets up this {@link SearchView} with an {@link SearchBar}, which will result in the {@link + * SearchView} being shown when the {@link SearchBar} is clicked. This behavior will be set up + * automatically if the {@link SearchBar} and {@link SearchView} are in a {@link + * CoordinatorLayout} and the {@link SearchView} is anchored to the {@link SearchBar}. + */ + public void setUpWithSearchBar(@Nullable SearchBar searchBar) { + this.searchBar = searchBar; + searchViewAnimationHelper.setSearchBar(searchBar); + if (searchBar != null) { + searchBar.setOnClickListener(v -> show()); + } + updateNavigationIconIfNeeded(); + setUpBackgroundViewElevationOverlay(); + } + + /** + * Add a header view to this {@link SearchView}, which will be placed above the search text area. + * + *

Note: due to complications with the expand/collapse animation, a header view is intended to + * be used with a standalone {@link SearchView} which slides up/down instead of morphing from an + * {@link SearchBar}. + */ + public void addHeaderView(@NonNull View headerView) { + headerContainer.addView(headerView); + headerContainer.setVisibility(VISIBLE); + } + + /** Remove a header view from the section above the search text area. */ + public void removeHeaderView(@NonNull View headerView) { + headerContainer.removeView(headerView); + if (headerContainer.getChildCount() == 0) { + headerContainer.setVisibility(GONE); + } + } + + /** Remove all header views from the section above the search text area. */ + public void removeAllHeaderViews() { + headerContainer.removeAllViews(); + headerContainer.setVisibility(GONE); + } + + /** + * Sets whether the navigation icon should be animated from the {@link SearchBar} to {@link + * SearchView}. + */ + public void setAnimatedNavigationIcon(boolean animatedNavigationIcon) { + this.animatedNavigationIcon = animatedNavigationIcon; + } + + /** + * Returns whether the navigation icon should be animated from the {@link SearchBar} to {@link + * SearchView}. + */ + public boolean isAnimatedNavigationIcon() { + return animatedNavigationIcon; + } + + /** + * Sets whether the menu items should be animated from the {@link SearchBar} to {@link + * SearchView}. + */ + public void setAnimatedMenuItems(boolean animatedMenuItems) { + this.animatedMenuItems = animatedMenuItems; + } + + /** + * Returns whether the menu items should be animated from the {@link SearchBar} to {@link + * SearchView}. + */ + public boolean isAnimatedMenuItems() { + return animatedMenuItems; + } + + /** Sets whether the soft keyboard should be shown when the {@link SearchView} is shown. */ + public void setAutoShowKeyboard(boolean autoShowKeyboard) { + this.autoShowKeyboard = autoShowKeyboard; + } + + /** Returns whether the soft keyboard should be shown when the {@link SearchView} is shown. */ + public boolean isAutoShowKeyboard() { + return autoShowKeyboard; + } + + /** Sets whether the soft keyboard should be shown with {@code WindowInsetsController}. */ + public void setUseWindowInsetsController(boolean useWindowInsetsController) { + this.useWindowInsetsController = useWindowInsetsController; + } + + /** Returns whether the soft keyboard should be shown with {@code WindowInsetsController}. */ + public boolean isUseWindowInsetsController() { + return useWindowInsetsController; + } + + /** Adds a listener to handle {@link SearchView} transitions such as showing and closing. */ + public void addTransitionListener(@NonNull TransitionListener transitionListener) { + transitionListeners.add(transitionListener); + } + + /** Removes a listener to handle {@link SearchView} transitions such as showing and closing. */ + public void removeTransitionListener(@NonNull TransitionListener transitionListener) { + transitionListeners.remove(transitionListener); + } + + /** Inflate a menu to provide additional options. */ + public void inflateMenu(@MenuRes int menuResId) { + toolbar.inflateMenu(menuResId); + } + + /** Set a listener to handle menu item clicks. */ + public void setOnMenuItemClickListener( + @Nullable OnMenuItemClickListener onMenuItemClickListener) { + toolbar.setOnMenuItemClickListener(onMenuItemClickListener); + } + + /** Returns the search prefix {@link TextView}, which appears before the main {@link EditText}. */ + @NonNull + public TextView getSearchPrefix() { + return searchPrefix; + } + + /** Sets the search prefix text. */ + public void setSearchPrefixText(@Nullable CharSequence searchPrefixText) { + searchPrefix.setText(searchPrefixText); + searchPrefix.setVisibility(TextUtils.isEmpty(searchPrefixText) ? GONE : VISIBLE); + } + + /** Returns the search prefix text. */ + @Nullable + public CharSequence getSearchPrefixText() { + return searchPrefix.getText(); + } + + /** Returns the {@link Toolbar} used by the {@link SearchView}. */ + @NonNull + public Toolbar getToolbar() { + return toolbar; + } + + /** Returns the main {@link EditText} which can be used for hint and search text. */ + @NonNull + public EditText getEditText() { + return editText; + } + + /** Returns the text of main {@link EditText}, which usually represents the search text. */ + @SuppressLint("KotlinPropertyAccess") // Editable extends CharSequence. + @Nullable + public Editable getText() { + return editText.getText(); + } + + /** Sets the text of main {@link EditText}. */ + @SuppressLint("KotlinPropertyAccess") // Editable extends CharSequence. + public void setText(@Nullable CharSequence text) { + editText.setText(text); + } + + /** Sets the text of main {@link EditText}. */ + public void setText(@StringRes int textResId) { + editText.setText(textResId); + } + + /** Clears the text of main {@link EditText}. */ + public void clearText() { + editText.setText(""); + } + + /** Returns the hint of main {@link EditText}. */ + @Nullable + public CharSequence getHint() { + return editText.getHint(); + } + + /** Sets the hint of main {@link EditText}. */ + public void setHint(@Nullable CharSequence hint) { + editText.setHint(hint); + } + + /** Sets the hint of main {@link EditText}. */ + public void setHint(@StringRes int hintResId) { + editText.setHint(hintResId); + } + + /** Returns the current value of this {@link SearchView}'s soft input mode. */ + @SuppressLint("KotlinPropertyAccess") // This is a not property. + public int getSoftInputMode() { + return softInputMode; + } + + /** + * Sets the soft input mode for this {@link SearchView}. This is important because the {@link + * SearchView} will use this to determine whether the keyboard should be shown/hidden at the same + * time as the expand/collapse animation, or if the keyboard should be staggered with the + * animation to avoid glitchiness due to a resize of the screen. This will be set automatically by + * the {@link SearchView} during initial render but make sure to invoke this if you are changing + * the soft input mode at runtime. + */ + public void setSoftInputMode(@Nullable Window window) { + if (window != null) { + this.softInputMode = window.getAttributes().softInputMode; + } + } + + /** + * Enables/disables the status bar spacer, which can be used in cases where the status bar is + * translucent and the {@link SearchView} should not overlap the status bar area. This will be set + * automatically by the {@link SearchView} during initial render based on {@link + * #shouldShowStatusBarSpacer(Window)}, but make sure to invoke this if you would like to override + * the default behavior. + */ + public void setStatusBarSpacerEnabled(boolean enabled) { + statusBarSpacer.setVisibility(enabled ? VISIBLE : GONE); + } + + /** Returns the current {@link TransitionState} for this {@link SearchView}. */ + @NonNull + public TransitionState getCurrentTransitionState() { + return currentTransitionState; + } + + void setTransitionState(@NonNull TransitionState state) { + if (currentTransitionState.equals(state)) { + return; + } + + TransitionState previousState = currentTransitionState; + currentTransitionState = state; + Set listeners = new LinkedHashSet<>(transitionListeners); + for (TransitionListener listener : listeners) { + listener.onStateChanged(this, previousState, state); + } + } + + /** Returns whether the {@link SearchView}'s main content view is shown or showing. */ + public boolean isShowing() { + return currentTransitionState.equals(TransitionState.SHOWN) + || currentTransitionState.equals(TransitionState.SHOWING); + } + + /** + * Shows the {@link SearchView} with an animation. + * + *

Note: the show animation will not be started if the {@link SearchView} is currently shown or + * showing. + */ + public void show() { + if (currentTransitionState.equals(TransitionState.SHOWN) + || currentTransitionState.equals(TransitionState.SHOWING)) { + return; + } + searchViewAnimationHelper.show(); + setModalForAccessibility(true); + } + + /** + * Hides the {@link SearchView} with an animation. + * + *

Note: the hide animation will not be started if the {@link SearchView} is currently hidden + * or hiding. + */ + public void hide() { + if (currentTransitionState.equals(TransitionState.HIDDEN) + || currentTransitionState.equals(TransitionState.HIDING)) { + return; + } + searchViewAnimationHelper.hide(); + setModalForAccessibility(false); + } + + /** Updates the visibility of the {@link SearchView} without an animation. */ + public void setVisible(boolean visible) { + boolean wasVisible = rootView.getVisibility() == VISIBLE; + rootView.setVisibility(visible ? VISIBLE : GONE); + updateNavigationIconProgressIfNeeded(); + if (wasVisible != visible) { + setModalForAccessibility(visible); + } + setTransitionState(visible ? TransitionState.SHOWN : TransitionState.HIDDEN); + } + + private void updateNavigationIconProgressIfNeeded() { + ImageButton backButton = ToolbarUtils.getNavigationIconButton(toolbar); + if (backButton == null) { + return; + } + + int progress = rootView.getVisibility() == VISIBLE ? 1 : 0; + Drawable drawable = DrawableCompat.unwrap(backButton.getDrawable()); + if (drawable instanceof DrawerArrowDrawable) { + ((DrawerArrowDrawable) drawable).setProgress(progress); + } + if (drawable instanceof FadeThroughDrawable) { + ((FadeThroughDrawable) drawable).setProgress(progress); + } + } + + /** + * Requests focus on the main {@link EditText} and shows the soft keyboard if automatic showing of + * the keyboard is enabled. + */ + void requestFocusAndShowKeyboardIfNeeded() { + if (autoShowKeyboard) { + requestFocusAndShowKeyboard(); + } + } + + /** Requests focus on the main {@link EditText} and shows the soft keyboard. */ + public void requestFocusAndShowKeyboard() { + editText.post( + () -> { + editText.requestFocus(); + ViewUtils.showKeyboard(editText, useWindowInsetsController); + }); + } + + /** Clears focus on the main {@link EditText} and hides the soft keyboard. */ + public void clearFocusAndHideKeyboard() { + editText.clearFocus(); + ViewUtils.hideKeyboard(editText, useWindowInsetsController); + } + + private static boolean shouldShowStatusBarSpacer(@Nullable Window window) { + if (window == null) { + return false; + } + WindowManager.LayoutParams lp = window.getAttributes(); + boolean translucentStatus = + VERSION.SDK_INT >= VERSION_CODES.KITKAT + && (lp.flags & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) + == WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; + boolean layoutNoLimits = + (lp.flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS) + == WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; + boolean edgeToEdge = + VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN + && (window.getDecorView().getSystemUiVisibility() & ViewUtils.EDGE_TO_EDGE_FLAGS) + == ViewUtils.EDGE_TO_EDGE_FLAGS; + return translucentStatus || layoutNoLimits || edgeToEdge; + } + + boolean isAdjustNothingSoftInputMode() { + return softInputMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; + } + + /** + * Sets whether the {@link SearchView} is modal for accessibility, i.e., whether views that are + * not nested within the {@link SearchView} are important for accessibility. + */ + public void setModalForAccessibility(boolean isSearchViewModal) { + ViewGroup rootView = (ViewGroup) this.getRootView(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && isSearchViewModal) { + childImportantForAccessibilityMap = new HashMap<>(rootView.getChildCount()); + } + updateChildImportantForAccessibility(rootView, isSearchViewModal); + + if (!isSearchViewModal) { + // When SearchView is not modal, reset the important for accessibility map. + childImportantForAccessibilityMap = null; + } + } + + /** + * Sets the 'touchscreenBlocksFocus' attribute of the nested toolbar. The attribute defaults to + * 'true' for API level 26+. We need to set it to 'false' if keyboard navigation is needed for the + * search results. + */ + public void setToolbarTouchscreenBlocksFocus(boolean touchscreenBlocksFocus) { + if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { + toolbar.setTouchscreenBlocksFocus(touchscreenBlocksFocus); + } + } + + @SuppressLint("InlinedApi") // View Compat will handle the differences. + private void updateChildImportantForAccessibility(ViewGroup parent, boolean isSearchViewModal) { + for (int i = 0; i < parent.getChildCount(); i++) { + final View child = parent.getChildAt(i); + if (child == this) { + continue; + } + + if (child.findViewById(this.rootView.getId()) != null) { + // If this child node contains SearchView, look at this node's children instead. + updateChildImportantForAccessibility((ViewGroup) child, isSearchViewModal); + continue; + } + + if (!isSearchViewModal) { + if (childImportantForAccessibilityMap != null + && childImportantForAccessibilityMap.containsKey(child)) { + // Restores the original important for accessibility value of the child view. + ViewCompat.setImportantForAccessibility( + child, childImportantForAccessibilityMap.get(child)); + } + } else { + // Saves the important for accessibility value of the child view. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + childImportantForAccessibilityMap.put(child, child.getImportantForAccessibility()); + } + + ViewCompat.setImportantForAccessibility( + child, ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + } + } + } + + /** Behavior that sets up an {@link SearchView} with an {@link SearchBar}. */ + public static class Behavior extends CoordinatorLayout.Behavior { + + public Behavior() {} + + public Behavior(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + @Override + public boolean onDependentViewChanged( + @NonNull CoordinatorLayout parent, @NonNull SearchView child, @NonNull View dependency) { + if (!child.isSetUpWithSearchBar() && dependency instanceof SearchBar) { + child.setUpWithSearchBar((SearchBar) dependency); + } + return false; + } + } + + /** Callback interface that provides important transition events for a {@link SearchView}. */ + public interface TransitionListener { + + /** Called when the given {@link SearchView SearchView's} transition state has changed. */ + void onStateChanged( + @NonNull SearchView searchView, + @NonNull TransitionState previousState, + @NonNull TransitionState newState); + } + + /** Enum that defines the possible transition states of an {@link SearchView}. */ + public enum TransitionState { + HIDING, + HIDDEN, + SHOWING, + SHOWN, + } + + @Override + @NonNull + protected Parcelable onSaveInstanceState() { + SavedState savedState = new SavedState(super.onSaveInstanceState()); + CharSequence text = getText(); + savedState.text = text == null ? null : text.toString(); + savedState.visibility = rootView.getVisibility(); + return savedState; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (!(state instanceof SavedState)) { + super.onRestoreInstanceState(state); + return; + } + + SavedState savedState = (SavedState) state; + super.onRestoreInstanceState(savedState.getSuperState()); + setText(savedState.text); + setVisible(savedState.visibility == VISIBLE); + } + + static class SavedState extends AbsSavedState { + + String text; + int visibility; + + public SavedState(Parcel source) { + this(source, null); + } + + public SavedState(Parcel source, @Nullable ClassLoader classLoader) { + super(source, classLoader); + text = source.readString(); + visibility = source.readInt(); + } + + public SavedState(Parcelable superState) { + super(superState); + } + + public static final Parcelable.Creator CREATOR = + new ClassLoaderCreator() { + + @Override + public SavedState createFromParcel(Parcel source, ClassLoader loader) { + return new SavedState(source, loader); + } + + @Override + public SavedState createFromParcel(Parcel source) { + return new SavedState(source); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(text); + dest.writeInt(visibility); + } + } +} diff --git a/lib/java/com/google/android/material/search/SearchViewAnimationHelper.java b/lib/java/com/google/android/material/search/SearchViewAnimationHelper.java new file mode 100644 index 00000000000..f0a830fff30 --- /dev/null +++ b/lib/java/com/google/android/material/search/SearchViewAnimationHelper.java @@ -0,0 +1,596 @@ +/* + * Copyright 2022 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 + * + * https://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.google.android.material.search; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import androidx.appcompat.graphics.drawable.DrawerArrowDrawable; +import androidx.appcompat.widget.ActionMenuView; +import androidx.appcompat.widget.Toolbar; +import android.view.Menu; +import android.view.View; +import android.view.ViewGroup.MarginLayoutParams; +import android.widget.EditText; +import android.widget.FrameLayout; +import android.widget.ImageButton; +import android.widget.TextView; +import androidx.core.graphics.drawable.DrawableCompat; +import androidx.core.view.MarginLayoutParamsCompat; +import androidx.core.view.ViewCompat; +import com.google.android.material.animation.AnimationUtils; +import com.google.android.material.internal.ClippableRoundedCornerLayout; +import com.google.android.material.internal.FadeThroughDrawable; +import com.google.android.material.internal.FadeThroughUpdateListener; +import com.google.android.material.internal.MultiViewUpdateListener; +import com.google.android.material.internal.RectEvaluator; +import com.google.android.material.internal.ReversableAnimatedValueInterpolator; +import com.google.android.material.internal.ToolbarUtils; +import com.google.android.material.internal.TouchObserverFrameLayout; +import com.google.android.material.internal.ViewUtils; + +/** Helper class for {@link SearchView} animations. */ +@SuppressWarnings("RestrictTo") +class SearchViewAnimationHelper { + + // Constants for show expand animation + private static final long SHOW_DURATION_MS = 300; + private static final long SHOW_CLEAR_BUTTON_ALPHA_DURATION_MS = 50; + private static final long SHOW_CLEAR_BUTTON_ALPHA_START_DELAY_MS = 250; + private static final long SHOW_CONTENT_ALPHA_DURATION_MS = 150; + private static final long SHOW_CONTENT_ALPHA_START_DELAY_MS = 75; + private static final long SHOW_CONTENT_SCALE_DURATION_MS = SHOW_DURATION_MS; + + // Constants for hide collapse animation + private static final long HIDE_DURATION_MS = 250; + private static final long HIDE_CLEAR_BUTTON_ALPHA_DURATION_MS = 42; + private static final long HIDE_CLEAR_BUTTON_ALPHA_START_DELAY_MS = 0; + private static final long HIDE_CONTENT_ALPHA_DURATION_MS = 83; + private static final long HIDE_CONTENT_ALPHA_START_DELAY_MS = 0; + private static final long HIDE_CONTENT_SCALE_DURATION_MS = HIDE_DURATION_MS; + + private static final float CONTENT_FROM_SCALE = 0.95f; + + // Constants for show translate animation + private static final long SHOW_TRANSLATE_DURATION_MS = 350; + private static final long SHOW_TRANSLATE_KEYBOARD_START_DELAY_MS = 150; + + // Constants for hide translate animation + private static final long HIDE_TRANSLATE_DURATION_MS = 300; + + private final SearchView searchView; + private final View scrim; + private final ClippableRoundedCornerLayout rootView; + private final FrameLayout headerContainer; + private final FrameLayout toolbarContainer; + private final Toolbar toolbar; + private final Toolbar dummyToolbar; + private final TextView searchPrefix; + private final EditText editText; + private final ImageButton clearButton; + private final View divider; + private final TouchObserverFrameLayout contentContainer; + + private SearchBar searchBar; + + SearchViewAnimationHelper(SearchView searchView) { + this.searchView = searchView; + this.scrim = searchView.scrim; + this.rootView = searchView.rootView; + this.headerContainer = searchView.headerContainer; + this.toolbarContainer = searchView.toolbarContainer; + this.toolbar = searchView.toolbar; + this.dummyToolbar = searchView.dummyToolbar; + this.searchPrefix = searchView.searchPrefix; + this.editText = searchView.editText; + this.clearButton = searchView.clearButton; + this.divider = searchView.divider; + this.contentContainer = searchView.contentContainer; + } + + void setSearchBar(SearchBar searchBar) { + this.searchBar = searchBar; + } + + void show() { + if (searchBar != null) { + startShowAnimationExpand(); + } else { + startShowAnimationTranslate(); + } + } + + void hide() { + if (searchBar != null) { + startHideAnimationCollapse(); + } else { + startHideAnimationTranslate(); + } + } + + private void startShowAnimationExpand() { + if (searchView.isAdjustNothingSoftInputMode()) { + searchView.requestFocusAndShowKeyboardIfNeeded(); + } + searchView.setTransitionState(SearchView.TransitionState.SHOWING); + setUpDummyToolbarIfNeeded(); + editText.setText(searchBar.getText()); + editText.setSelection(editText.getText().length()); + rootView.setVisibility(View.INVISIBLE); + rootView.post( + () -> { + AnimatorSet animatorSet = getExpandCollapseAnimatorSet(true); + animatorSet.addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + rootView.setVisibility(View.VISIBLE); + searchBar.stopOnLoadAnimation(); + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!searchView.isAdjustNothingSoftInputMode()) { + searchView.requestFocusAndShowKeyboardIfNeeded(); + } + searchView.setTransitionState(SearchView.TransitionState.SHOWN); + } + }); + animatorSet.start(); + }); + } + + private void startHideAnimationCollapse() { + if (searchView.isAdjustNothingSoftInputMode()) { + searchView.clearFocusAndHideKeyboard(); + } + AnimatorSet animatorSet = getExpandCollapseAnimatorSet(false); + animatorSet.addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + searchView.setTransitionState(SearchView.TransitionState.HIDING); + } + + @Override + public void onAnimationEnd(Animator animation) { + rootView.setVisibility(View.GONE); + if (!searchView.isAdjustNothingSoftInputMode()) { + searchView.clearFocusAndHideKeyboard(); + } + searchView.setTransitionState(SearchView.TransitionState.HIDDEN); + } + }); + animatorSet.start(); + } + + private void startShowAnimationTranslate() { + if (searchView.isAdjustNothingSoftInputMode()) { + searchView.postDelayed( + searchView::requestFocusAndShowKeyboardIfNeeded, + SHOW_TRANSLATE_KEYBOARD_START_DELAY_MS); + } + rootView.setVisibility(View.INVISIBLE); + rootView.post( + () -> { + rootView.setTranslationY(rootView.getHeight()); + AnimatorSet animatorSet = getTranslateAnimatorSet(true); + animatorSet.addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + rootView.setVisibility(View.VISIBLE); + searchView.setTransitionState(SearchView.TransitionState.SHOWING); + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!searchView.isAdjustNothingSoftInputMode()) { + searchView.requestFocusAndShowKeyboardIfNeeded(); + } + searchView.setTransitionState(SearchView.TransitionState.SHOWN); + } + }); + animatorSet.start(); + }); + } + + private void startHideAnimationTranslate() { + if (searchView.isAdjustNothingSoftInputMode()) { + searchView.clearFocusAndHideKeyboard(); + } + AnimatorSet animatorSet = getTranslateAnimatorSet(false); + animatorSet.addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + searchView.setTransitionState(SearchView.TransitionState.HIDING); + } + + @Override + public void onAnimationEnd(Animator animation) { + rootView.setVisibility(View.GONE); + if (!searchView.isAdjustNothingSoftInputMode()) { + searchView.clearFocusAndHideKeyboard(); + } + searchView.setTransitionState(SearchView.TransitionState.HIDDEN); + } + }); + animatorSet.start(); + } + + private AnimatorSet getTranslateAnimatorSet(boolean show) { + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.playTogether(getTranslationYAnimator()); + addBackButtonProgressAnimatorIfNeeded(animatorSet); + animatorSet.setInterpolator( + ReversableAnimatedValueInterpolator.of(show, AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)); + animatorSet.setDuration(show ? SHOW_TRANSLATE_DURATION_MS : HIDE_TRANSLATE_DURATION_MS); + return animatorSet; + } + + private Animator getTranslationYAnimator() { + ValueAnimator animator = ValueAnimator.ofFloat(rootView.getHeight(), 0); + animator.addUpdateListener(MultiViewUpdateListener.translationYListener(rootView)); + return animator; + } + + private AnimatorSet getExpandCollapseAnimatorSet(boolean show) { + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.playTogether( + getScrimAlphaAnimator(show), + getRootViewAnimator(show), + getClearButtonAnimator(show), + getContentAnimator(show), + getButtonsAnimator(show), + getHeaderContainerAnimator(show), + getDummyToolbarAnimator(show), + getActionMenuViewsAlphaAnimator(show), + getEditTextAnimator(show), + getSearchPrefixAnimator(show)); + animatorSet.addListener( + new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + setContentViewsAlpha(show ? 0 : 1); + } + + @Override + public void onAnimationEnd(Animator animation) { + setContentViewsAlpha(show ? 1 : 0); + if (show) { + // After expanding, we should reset the clip bounds so it can react to screen or + // layout changes. Otherwise it will result in wrong clipping on the layout. + rootView.resetClipBoundsAndCornerRadius(); + } + } + }); + return animatorSet; + } + + private void setContentViewsAlpha(float alpha) { + clearButton.setAlpha(alpha); + divider.setAlpha(alpha); + contentContainer.setAlpha(alpha); + setActionMenuViewAlphaIfNeeded(alpha); + } + + private void setActionMenuViewAlphaIfNeeded(float alpha) { + if (searchView.isAnimatedMenuItems()) { + ActionMenuView actionMenuView = ToolbarUtils.getActionMenuView(toolbar); + if (actionMenuView != null) { + actionMenuView.setAlpha(alpha); + } + } + } + + private Animator getScrimAlphaAnimator(boolean show) { + TimeInterpolator interpolator = + show ? AnimationUtils.LINEAR_INTERPOLATOR : AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR; + + ValueAnimator animator = ValueAnimator.ofFloat(0, 1); + animator.setDuration(show ? SHOW_DURATION_MS : HIDE_DURATION_MS); + animator.setInterpolator(ReversableAnimatedValueInterpolator.of(show, interpolator)); + animator.addUpdateListener(MultiViewUpdateListener.alphaListener(scrim)); + return animator; + } + + private Animator getRootViewAnimator(boolean show) { + Rect toClipBounds = ViewUtils.calculateRectFromBounds(searchView); + Rect fromClipBounds = calculateFromClipBounds(); + Rect clipBounds = new Rect(fromClipBounds); + + float initialCornerRadius = searchBar.getCornerSize(); + + ValueAnimator animator = + ValueAnimator.ofObject(new RectEvaluator(clipBounds), fromClipBounds, toClipBounds); + animator.addUpdateListener( + valueAnimator -> { + float cornerRadius = initialCornerRadius * (1 - valueAnimator.getAnimatedFraction()); + rootView.updateClipBoundsAndCornerRadius(clipBounds, cornerRadius); + }); + animator.setDuration(show ? SHOW_DURATION_MS : HIDE_DURATION_MS); + animator.setInterpolator( + ReversableAnimatedValueInterpolator.of(show, AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)); + return animator; + } + + private Rect calculateFromClipBounds() { + int[] searchBarAbsolutePosition = new int[2]; + searchBar.getLocationOnScreen(searchBarAbsolutePosition); + int searchBarAbsoluteLeft = searchBarAbsolutePosition[0]; + int searchBarAbsoluteTop = searchBarAbsolutePosition[1]; + + // Use rootView to handle potential fitsSystemWindows padding applied to parent searchView. + int[] searchViewAbsolutePosition = new int[2]; + rootView.getLocationOnScreen(searchViewAbsolutePosition); + int searchViewAbsoluteLeft = searchViewAbsolutePosition[0]; + int searchViewAbsoluteTop = searchViewAbsolutePosition[1]; + + int fromLeft = searchBarAbsoluteLeft - searchViewAbsoluteLeft; + int fromTop = searchBarAbsoluteTop - searchViewAbsoluteTop; + int fromRight = fromLeft + searchBar.getWidth(); + int fromBottom = fromTop + searchBar.getHeight(); + + return new Rect(fromLeft, fromTop, fromRight, fromBottom); + } + + private Animator getClearButtonAnimator(boolean show) { + ValueAnimator animator = ValueAnimator.ofFloat(0, 1); + animator.setDuration( + show ? SHOW_CLEAR_BUTTON_ALPHA_DURATION_MS : HIDE_CLEAR_BUTTON_ALPHA_DURATION_MS); + animator.setStartDelay( + show ? SHOW_CLEAR_BUTTON_ALPHA_START_DELAY_MS : HIDE_CLEAR_BUTTON_ALPHA_START_DELAY_MS); + animator.setInterpolator( + ReversableAnimatedValueInterpolator.of(show, AnimationUtils.LINEAR_INTERPOLATOR)); + animator.addUpdateListener(MultiViewUpdateListener.alphaListener(clearButton)); + return animator; + } + + private Animator getButtonsAnimator(boolean show) { + AnimatorSet animatorSet = new AnimatorSet(); + addBackButtonTranslationAnimatorIfNeeded(animatorSet); + addBackButtonProgressAnimatorIfNeeded(animatorSet); + addActionMenuViewAnimatorIfNeeded(animatorSet); + animatorSet.setDuration(show ? SHOW_DURATION_MS : HIDE_DURATION_MS); + animatorSet.setInterpolator( + ReversableAnimatedValueInterpolator.of(show, AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)); + return animatorSet; + } + + private void addBackButtonTranslationAnimatorIfNeeded(AnimatorSet animatorSet) { + ImageButton backButton = ToolbarUtils.getNavigationIconButton(toolbar); + if (backButton == null) { + return; + } + + ValueAnimator backButtonAnimatorX = + ValueAnimator.ofFloat(getFromTranslationXStart(backButton), 0); + backButtonAnimatorX.addUpdateListener(MultiViewUpdateListener.translationXListener(backButton)); + + ValueAnimator backButtonAnimatorY = ValueAnimator.ofFloat(getFromTranslationY(), 0); + backButtonAnimatorY.addUpdateListener(MultiViewUpdateListener.translationYListener(backButton)); + + animatorSet.playTogether(backButtonAnimatorX, backButtonAnimatorY); + } + + private void addBackButtonProgressAnimatorIfNeeded(AnimatorSet animatorSet) { + ImageButton backButton = ToolbarUtils.getNavigationIconButton(toolbar); + if (backButton == null) { + return; + } + + Drawable drawable = DrawableCompat.unwrap(backButton.getDrawable()); + if (searchView.isAnimatedNavigationIcon()) { + addDrawerArrowDrawableAnimatorIfNeeded(animatorSet, drawable); + addFadeThroughDrawableAnimatorIfNeeded(animatorSet, drawable); + } else { + setFullDrawableProgressIfNeeded(drawable); + } + } + + private void addDrawerArrowDrawableAnimatorIfNeeded(AnimatorSet animatorSet, Drawable drawable) { + if (drawable instanceof DrawerArrowDrawable) { + DrawerArrowDrawable drawerArrowDrawable = (DrawerArrowDrawable) drawable; + ValueAnimator animator = ValueAnimator.ofFloat(0, 1); + animator.addUpdateListener( + animation -> drawerArrowDrawable.setProgress(animation.getAnimatedFraction())); + animatorSet.playTogether(animator); + } + } + + private void addFadeThroughDrawableAnimatorIfNeeded(AnimatorSet animatorSet, Drawable drawable) { + if (drawable instanceof FadeThroughDrawable) { + FadeThroughDrawable fadeThroughDrawable = (FadeThroughDrawable) drawable; + ValueAnimator animator = ValueAnimator.ofFloat(0, 1); + animator.addUpdateListener( + animation -> fadeThroughDrawable.setProgress(animation.getAnimatedFraction())); + animatorSet.playTogether(animator); + } + } + + private void setFullDrawableProgressIfNeeded(Drawable drawable) { + if (drawable instanceof DrawerArrowDrawable) { + ((DrawerArrowDrawable) drawable).setProgress(1); + } + if (drawable instanceof FadeThroughDrawable) { + ((FadeThroughDrawable) drawable).setProgress(1); + } + } + + private void addActionMenuViewAnimatorIfNeeded(AnimatorSet animatorSet) { + ActionMenuView actionMenuView = ToolbarUtils.getActionMenuView(toolbar); + if (actionMenuView == null) { + return; + } + + ValueAnimator actionMenuViewAnimatorX = + ValueAnimator.ofFloat(getFromTranslationXEnd(actionMenuView), 0); + actionMenuViewAnimatorX.addUpdateListener( + MultiViewUpdateListener.translationXListener(actionMenuView)); + + ValueAnimator actionMenuViewAnimatorY = ValueAnimator.ofFloat(getFromTranslationY(), 0); + actionMenuViewAnimatorY.addUpdateListener( + MultiViewUpdateListener.translationYListener(actionMenuView)); + + animatorSet.playTogether(actionMenuViewAnimatorX, actionMenuViewAnimatorY); + } + + private Animator getDummyToolbarAnimator(boolean show) { + return getTranslationAnimator(show, false, dummyToolbar); + } + + private Animator getHeaderContainerAnimator(boolean show) { + return getTranslationAnimator(show, false, headerContainer); + } + + private Animator getActionMenuViewsAlphaAnimator(boolean show) { + ValueAnimator animator = ValueAnimator.ofFloat(0, 1); + animator.setDuration(show ? SHOW_DURATION_MS : HIDE_DURATION_MS); + animator.setInterpolator( + ReversableAnimatedValueInterpolator.of(show, AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)); + + if (searchView.isAnimatedMenuItems()) { + ActionMenuView dummyActionMenuView = ToolbarUtils.getActionMenuView(dummyToolbar); + ActionMenuView actionMenuView = ToolbarUtils.getActionMenuView(toolbar); + animator.addUpdateListener( + new FadeThroughUpdateListener(dummyActionMenuView, actionMenuView)); + } + + return animator; + } + + private Animator getSearchPrefixAnimator(boolean show) { + return getTranslationAnimator(show, true, searchPrefix); + } + + private Animator getEditTextAnimator(boolean show) { + return getTranslationAnimator(show, true, editText); + } + + private Animator getContentAnimator(boolean show) { + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.playTogether( + getContentAlphaAnimator(show), getDividerAnimator(show), getContentScaleAnimator(show)); + return animatorSet; + } + + private Animator getContentAlphaAnimator(boolean show) { + ValueAnimator animatorAlpha = ValueAnimator.ofFloat(0, 1); + animatorAlpha.setDuration( + show ? SHOW_CONTENT_ALPHA_DURATION_MS : HIDE_CONTENT_ALPHA_DURATION_MS); + animatorAlpha.setStartDelay( + show ? SHOW_CONTENT_ALPHA_START_DELAY_MS : HIDE_CONTENT_ALPHA_START_DELAY_MS); + animatorAlpha.setInterpolator( + ReversableAnimatedValueInterpolator.of(show, AnimationUtils.LINEAR_INTERPOLATOR)); + animatorAlpha.addUpdateListener( + MultiViewUpdateListener.alphaListener(divider, contentContainer)); + return animatorAlpha; + } + + private Animator getDividerAnimator(boolean show) { + float dividerTranslationY = + (float) contentContainer.getHeight() * (1f - CONTENT_FROM_SCALE) / 2f; + + ValueAnimator animatorDivider = ValueAnimator.ofFloat(dividerTranslationY, 0); + animatorDivider.setDuration( + show ? SHOW_CONTENT_SCALE_DURATION_MS : HIDE_CONTENT_SCALE_DURATION_MS); + animatorDivider.setInterpolator( + ReversableAnimatedValueInterpolator.of(show, AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)); + animatorDivider.addUpdateListener(MultiViewUpdateListener.translationYListener(divider)); + return animatorDivider; + } + + private Animator getContentScaleAnimator(boolean show) { + ValueAnimator animatorScale = ValueAnimator.ofFloat(CONTENT_FROM_SCALE, 1); + animatorScale.setDuration( + show ? SHOW_CONTENT_SCALE_DURATION_MS : HIDE_CONTENT_SCALE_DURATION_MS); + animatorScale.setInterpolator( + ReversableAnimatedValueInterpolator.of(show, AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)); + animatorScale.addUpdateListener(MultiViewUpdateListener.scaleListener(contentContainer)); + return animatorScale; + } + + private Animator getTranslationAnimator(boolean show, boolean anchoredToStart, View view) { + int startX = anchoredToStart ? getFromTranslationXStart(view) : getFromTranslationXEnd(view); + ValueAnimator animatorX = ValueAnimator.ofFloat(startX, 0); + animatorX.addUpdateListener(MultiViewUpdateListener.translationXListener(view)); + + ValueAnimator animatorY = ValueAnimator.ofFloat(getFromTranslationY(), 0); + animatorY.addUpdateListener(MultiViewUpdateListener.translationYListener(view)); + + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.playTogether(animatorX, animatorY); + animatorSet.setDuration(show ? SHOW_DURATION_MS : HIDE_DURATION_MS); + animatorSet.setInterpolator( + ReversableAnimatedValueInterpolator.of(show, AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)); + return animatorSet; + } + + private int getFromTranslationXStart(View view) { + int marginStart = + MarginLayoutParamsCompat.getMarginStart((MarginLayoutParams) view.getLayoutParams()); + int paddingStart = ViewCompat.getPaddingStart(searchBar); + return ViewUtils.isLayoutRtl(searchBar) + ? searchBar.getWidth() - searchBar.getRight() + marginStart - paddingStart + : searchBar.getLeft() - marginStart + paddingStart; + } + + private int getFromTranslationXEnd(View view) { + int marginEnd = + MarginLayoutParamsCompat.getMarginEnd((MarginLayoutParams) view.getLayoutParams()); + return ViewUtils.isLayoutRtl(searchBar) + ? searchBar.getLeft() - marginEnd + : searchBar.getRight() - searchView.getWidth() + marginEnd; + } + + private int getFromTranslationY() { + int toolbarMiddleY = (toolbarContainer.getTop() + toolbarContainer.getBottom()) / 2; + int searchBarMiddleY = (searchBar.getTop() + searchBar.getBottom()) / 2; + return searchBarMiddleY - toolbarMiddleY; + } + + private void setUpDummyToolbarIfNeeded() { + Menu menu = dummyToolbar.getMenu(); + if (menu != null) { + menu.clear(); + } + if (searchBar.getMenuResId() != -1 && searchView.isAnimatedMenuItems()) { + dummyToolbar.inflateMenu(searchBar.getMenuResId()); + setMenuItemsNotClickable(dummyToolbar); + dummyToolbar.setVisibility(View.VISIBLE); + } else { + dummyToolbar.setVisibility(View.GONE); + } + } + + private void setMenuItemsNotClickable(Toolbar toolbar) { + ActionMenuView actionMenuView = ToolbarUtils.getActionMenuView(toolbar); + if (actionMenuView != null) { + for (int i = 0; i < actionMenuView.getChildCount(); i++) { + View menuItem = actionMenuView.getChildAt(i); + menuItem.setClickable(false); + menuItem.setFocusable(false); + menuItem.setFocusableInTouchMode(false); + } + } + } +} diff --git a/lib/java/com/google/android/material/search/res-public/values/public.xml b/lib/java/com/google/android/material/search/res-public/values/public.xml new file mode 100644 index 00000000000..38a2c814d4f --- /dev/null +++ b/lib/java/com/google/android/material/search/res-public/values/public.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/java/com/google/android/material/search/res/drawable/ic_arrow_back_black_24.xml b/lib/java/com/google/android/material/search/res/drawable/ic_arrow_back_black_24.xml new file mode 100644 index 00000000000..8bcc10ec26b --- /dev/null +++ b/lib/java/com/google/android/material/search/res/drawable/ic_arrow_back_black_24.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/lib/java/com/google/android/material/search/res/drawable/ic_clear_black_24.xml b/lib/java/com/google/android/material/search/res/drawable/ic_clear_black_24.xml new file mode 100644 index 00000000000..6d123d5a262 --- /dev/null +++ b/lib/java/com/google/android/material/search/res/drawable/ic_clear_black_24.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/lib/java/com/google/android/material/search/res/drawable/ic_search_black_24.xml b/lib/java/com/google/android/material/search/res/drawable/ic_search_black_24.xml new file mode 100644 index 00000000000..432276eb5e4 --- /dev/null +++ b/lib/java/com/google/android/material/search/res/drawable/ic_search_black_24.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/lib/java/com/google/android/material/search/res/layout/mtrl_search_bar.xml b/lib/java/com/google/android/material/search/res/layout/mtrl_search_bar.xml new file mode 100644 index 00000000000..c3b6dee3e71 --- /dev/null +++ b/lib/java/com/google/android/material/search/res/layout/mtrl_search_bar.xml @@ -0,0 +1,25 @@ + + + + diff --git a/lib/java/com/google/android/material/search/res/layout/mtrl_search_view.xml b/lib/java/com/google/android/material/search/res/layout/mtrl_search_view.xml new file mode 100644 index 00000000000..b79469a9c36 --- /dev/null +++ b/lib/java/com/google/android/material/search/res/layout/mtrl_search_view.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/java/com/google/android/material/search/res/values/attrs.xml b/lib/java/com/google/android/material/search/res/values/attrs.xml new file mode 100644 index 00000000000..fa046f89f03 --- /dev/null +++ b/lib/java/com/google/android/material/search/res/values/attrs.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/java/com/google/android/material/search/res/values/dimens.xml b/lib/java/com/google/android/material/search/res/values/dimens.xml new file mode 100644 index 00000000000..b783e6be371 --- /dev/null +++ b/lib/java/com/google/android/material/search/res/values/dimens.xml @@ -0,0 +1,31 @@ + + + + + 48dp + 3dp + 8dp + 16sp + 16dp + 16dp + 8dp + 4dp + + 6dp + 1dp + + diff --git a/lib/java/com/google/android/material/search/res/values/strings.xml b/lib/java/com/google/android/material/search/res/values/strings.xml new file mode 100644 index 00000000000..aaf98dd8c5e --- /dev/null +++ b/lib/java/com/google/android/material/search/res/values/strings.xml @@ -0,0 +1,39 @@ + + + + + + com.google.android.material.search.SearchBar$ScrollingViewBehavior + + + + com.google.android.libraries.material.search.SearchView$Behavior + + + + Back + + Clear text + diff --git a/lib/java/com/google/android/material/search/res/values/styles.xml b/lib/java/com/google/android/material/search/res/values/styles.xml new file mode 100644 index 00000000000..79827908fcd --- /dev/null +++ b/lib/java/com/google/android/material/search/res/values/styles.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/java/com/google/android/material/search/res/values/tokens.xml b/lib/java/com/google/android/material/search/res/values/tokens.xml new file mode 100644 index 00000000000..aed42b28332 --- /dev/null +++ b/lib/java/com/google/android/material/search/res/values/tokens.xml @@ -0,0 +1,73 @@ + + + + + + + + +