0

Add a shadow to the follow accelerator button.

Due to optical illusions, the shadow using android:elevation did
not show up since the button was a darker color.  Instead we now use
a 9-patch based approach.


Bug: b/188184619
Change-Id: I303f08c87d8b9c0eb20a56993f5c5a99401f8d2a

be able to do the shadow.  I have followed the instructions at
and run tools/resources/optimize-png-files.sh to make the bitmaps
as small as possible.

Binary-Size: Size increase is unavoidable.  We have added bitmaps to
https: //chromium.googlesource.com/chromium/src/+/HEAD/docs/speed/binary_size/optimization_advice.md#optimizing-images
Change-Id: I303f08c87d8b9c0eb20a56993f5c5a99401f8d2a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3072888
Commit-Queue: Peter Williamson <petewil@chromium.org>
Reviewed-by: Cathy Li <chili@chromium.org>
Reviewed-by: Theresa  <twellington@chromium.org>
Reviewed-by: Andrew Grieve <agrieve@chromium.org>
Cr-Commit-Position: refs/heads/main@{#914970}
This commit is contained in:
Pete Williamson
2021-08-24 23:42:18 +00:00
committed by Chromium LUCI CQ
parent 1a848e12fb
commit b1733c7bb7
10 changed files with 59 additions and 22 deletions
chrome/browser/feed/android
components/browser_ui/widget/android
BUILD.gn
java
res
src
org
chromium
components
browser_ui
widget
textbubble

@ -16,6 +16,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/feed/VideoPreviewsType.java",
"java/src/org/chromium/chrome/browser/feed/settings/FeedAutoplaySettingsFragment.java",
"java/src/org/chromium/chrome/browser/feed/settings/RadioButtonGroupVideoPreviewsPreference.java",
"java/src/org/chromium/chrome/browser/feed/webfeed/ShadowedClickableTextBubble.java",
"java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedBridge.java",
"java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedDialogContents.java",
"java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedDialogCoordinator.java",
@ -54,6 +55,7 @@ android_library("java") {
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
"//third_party/androidx:androidx_browser_browser_java",
"//third_party/androidx:androidx_core_core_java",
"//third_party/androidx:androidx_preference_preference_java",
"//ui/android:ui_no_recycler_view_java",
"//url:gurl_java",
@ -65,6 +67,10 @@ android_library("java") {
android_resources("web_feed_java_resources") {
sources = [
"java/res/color/menu_footer_chip_background.xml",
"java/res/drawable-hdpi/follow_accelerator_shadow.9.png",
"java/res/drawable-mdpi/follow_accelerator_shadow.9.png",
"java/res/drawable-xhdpi/follow_accelerator_shadow.9.png",
"java/res/drawable-xxhdpi/follow_accelerator_shadow.9.png",
"java/res/drawable/web_feed_post_follow_illustration.xml",
"java/res/layout/feed_management_activity.xml",
"java/res/layout/feed_management_list_item.xml",

Binary file not shown.

After

(image error) Size: 2.2 KiB

Binary file not shown.

After

(image error) Size: 1.3 KiB

Binary file not shown.

After

(image error) Size: 2.9 KiB

Binary file not shown.

After

(image error) Size: 7.0 KiB

@ -1,19 +1,29 @@
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.browser_ui.widget.textbubble;
package org.chromium.chrome.browser.feed.webfeed;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.View;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import androidx.core.content.res.ResourcesCompat;
import org.chromium.components.browser_ui.widget.R;
import org.chromium.components.browser_ui.widget.textbubble.TextBubble;
import org.chromium.ui.widget.LoadingView;
import org.chromium.ui.widget.RectProvider;
/**
* Provides a text bubble with a dark shadow to support a medium dark color. We need a darker
* shadow than other images such as navigation_bubble_shadow provide to prevent the optical illusion
* of the shadow being a part of the bitmap. However, if we add the images for the new shadow to
* ClickableTextBubble, they also get used by WebLayer, which doesn't need this shadow. To prevent
* increasing the size of the chrome APK, we include the bitmaps and the class to use them here with
* the other feed code.
*
* UI component that handles showing a clickable text callout bubble.
*
* <p>This has special styling specific to clickable text bubbles:
@ -21,7 +31,6 @@ import org.chromium.ui.widget.RectProvider;
* <li>No arrow
* <li>Rounder corners
* <li>Smaller padding
* //TODO(sophey): Implement shadow once 9-patches are available.
* <li>Shadow
* <li>Optional loading UI
* </ul>
@ -34,7 +43,7 @@ import org.chromium.ui.widget.RectProvider;
* element). Example below:
*
* <pre>{@code
* ClickableTextBubble clickableTextBubble;
* ShadowedClickableTextBubble clickableTextBubble;
* OnTouchListener onTouchListener = (view, motionEvent) -> {
* performPotentiallyLongRequest();
* clickableTextBubble.showLoadingUI(loadingViewContentDescriptionId);
@ -49,11 +58,13 @@ import org.chromium.ui.widget.RectProvider;
* }
* }</pre>
*/
public class ClickableTextBubble extends TextBubble {
public class ShadowedClickableTextBubble extends TextBubble {
private final Context mContext;
private final LoadingView mLoadingView;
private final boolean mInverseColor;
/**
* Constructs a {@link ClickableTextBubble} instance.
* Constructs a {@link ShadowedClickableTextBubble} instance.
*
* @param context Context to draw resources from.
* @param rootView The {@link View} to use for size calculations and for display.
@ -65,17 +76,27 @@ public class ClickableTextBubble extends TextBubble {
* text and dismiss UX.
* @param onTouchListener The callback for all touch events being dispatched to the bubble.
*/
public ClickableTextBubble(Context context, View rootView, @StringRes int stringId,
public ShadowedClickableTextBubble(Context context, View rootView, @StringRes int stringId,
@StringRes int accessibilityStringId, RectProvider anchorRectProvider,
@DrawableRes int imageDrawableId, boolean isAccessibilityEnabled,
View.OnTouchListener onTouchListener) {
View.OnTouchListener onTouchListener, boolean inverseColor) {
super(context, rootView, stringId, accessibilityStringId, /*showArrow=*/false,
anchorRectProvider, imageDrawableId, /*isRoundBubble=*/true, /*inverseColor=*/false,
isAccessibilityEnabled);
anchorRectProvider, imageDrawableId, /*isRoundBubble=*/true,
/*inverseColor=*/inverseColor, isAccessibilityEnabled);
mContext = context;
mInverseColor = inverseColor;
setTouchInterceptor(onTouchListener);
mLoadingView = mContentView.findViewById(R.id.loading_view);
}
/** Get the backgound to use. We use a color button with a dark shadow. */
@Override
public Drawable getBackground(Context context, boolean showArrow, boolean isRoundBubble) {
Drawable background = ResourcesCompat.getDrawable(
context.getResources(), R.drawable.follow_accelerator_shadow, null);
return background;
}
/**
* Replaces image with loading spinner. Dismisses the entire button when loading spinner is
* hidden.

@ -16,7 +16,6 @@ import org.chromium.chrome.browser.user_education.IPHCommandBuilder;
import org.chromium.chrome.browser.user_education.UserEducationHelper;
import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter;
import org.chromium.components.browser_ui.widget.textbubble.ClickableTextBubble;
import org.chromium.components.browser_ui.widget.textbubble.TextBubble;
import org.chromium.components.feature_engagement.FeatureConstants;
import org.chromium.components.feature_engagement.Tracker;
@ -45,7 +44,7 @@ class WebFeedFollowIntroView {
private final Tracker mFeatureEngagementTracker;
private final Runnable mIntroDismissedCallback;
private ClickableTextBubble mFollowBubble;
private ShadowedClickableTextBubble mFollowBubble;
private final int mShowTimeoutMillis;
/**
@ -76,9 +75,10 @@ class WebFeedFollowIntroView {
return;
}
mFollowBubble = new ClickableTextBubble(mActivity, mMenuButtonAnchorView,
mFollowBubble = new ShadowedClickableTextBubble(mActivity, mMenuButtonAnchorView,
R.string.menu_follow, R.string.menu_follow, createRectProvider(), R.drawable.ic_add,
ChromeAccessibilityUtil.get().isAccessibilityEnabled(), onTouchListener);
ChromeAccessibilityUtil.get().isAccessibilityEnabled(), onTouchListener,
/*inverseColor*/ false);
mFollowBubble.addOnDismissListener(this::introDismissed);
// TODO(crbug/1152592): Figure out a way to dismiss on outside taps as well.
mFollowBubble.setAutoDismissTimeout(mShowTimeoutMillis);

@ -104,7 +104,6 @@ android_library("java") {
"java/src/org/chromium/components/browser_ui/widget/text/TextViewWithCompoundDrawables.java",
"java/src/org/chromium/components/browser_ui/widget/text/VerticallyFixedEditText.java",
"java/src/org/chromium/components/browser_ui/widget/textbubble/ArrowBubbleDrawable.java",
"java/src/org/chromium/components/browser_ui/widget/textbubble/ClickableTextBubble.java",
"java/src/org/chromium/components/browser_ui/widget/textbubble/TextBubble.java",
"java/src/org/chromium/components/browser_ui/widget/tile/TileView.java",
"java/src/org/chromium/components/browser_ui/widget/tile/TileViewBinder.java",

@ -50,4 +50,4 @@
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.TextMediumThick.Primary.Inverse" />
</LinearLayout>
</LinearLayout>

@ -57,10 +57,11 @@ public class TextBubble implements AnchoredPopupWindow.LayoutObserver {
private final AnchoredPopupWindow mPopupWindow;
/** The {@link Drawable} that is responsible for drawing the bubble and the arrow. */
private final ArrowBubbleDrawable mBubbleDrawable;
@Nullable
private ArrowBubbleDrawable mBubbleDrawable;
/** The {@link Drawable} that precedes the text in the bubble. */
private final Drawable mImageDrawable;
protected final Drawable mImageDrawable;
private final Runnable mDismissRunnable = new Runnable() {
@Override
@ -253,8 +254,9 @@ public class TextBubble implements AnchoredPopupWindow.LayoutObserver {
mInverseColor = inverseColor;
mIsAccessibilityEnabled = isAccessibilityEnabled;
mBubbleDrawable = new ArrowBubbleDrawable(context, isRoundBubble);
mBubbleDrawable.setShowArrow(showArrow);
// For round, arrowless bubbles, we use a specialized background instead of the
// ArrowBubbleDrawable.
Drawable backgroundDrawable = getBackground(mContext, showArrow, isRoundBubble);
mContentView = createContentView();
// On some versions of Android, the LayoutParams aren't set until after the popup window
@ -264,7 +266,7 @@ public class TextBubble implements AnchoredPopupWindow.LayoutObserver {
new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
mPopupWindow = new AnchoredPopupWindow(
context, rootView, mBubbleDrawable, mContentView, anchorRectProvider);
context, rootView, backgroundDrawable, mContentView, anchorRectProvider);
mPopupWindow.setMargin(
context.getResources().getDimensionPixelSize(R.dimen.text_bubble_margin));
mPopupWindow.setPreferredHorizontalOrientation(
@ -277,15 +279,21 @@ public class TextBubble implements AnchoredPopupWindow.LayoutObserver {
addOnDismissListener(mDismissListener);
if (mIsAccessibilityEnabled) setDismissOnTouchInteraction(true);
}
/** Get the background to use. May be overridden by subclasses. */
protected Drawable getBackground(Context context, boolean showArrow, boolean isRoundBubble) {
mBubbleDrawable = new ArrowBubbleDrawable(mContext, isRoundBubble);
mBubbleDrawable.setShowArrow(showArrow);
// Set predefined styles for the TextBubble.
if (inverseColor) {
if (mInverseColor) {
mBubbleDrawable.setBubbleColor(ApiCompatibilityUtils.getColor(
mContext.getResources(), R.color.default_bg_color));
} else {
mBubbleDrawable.setBubbleColor(ApiCompatibilityUtils.getColor(
mContext.getResources(), R.color.default_control_color_active));
}
return mBubbleDrawable;
}
/** Shows the bubble. Will have no effect if the bubble is already showing. */
@ -399,6 +407,9 @@ public class TextBubble implements AnchoredPopupWindow.LayoutObserver {
@Override
public void onPreLayoutChange(
boolean positionBelow, int x, int y, int width, int height, Rect anchorRect) {
// mBubbleDrawable might not be in use if a subclass replaces the drawable.
if (mBubbleDrawable == null) return;
int arrowXOffset = 0;
if (mBubbleDrawable.isShowingArrow()) {
arrowXOffset = anchorRect.centerX() - x;