Commit cdc959a4 authored by Clemens Arbesser's avatar Clemens Arbesser Committed by Commit Bot

[Autofill Assistant] Add optional image and text to full overlay.

This CL adds support for an additional image with an optional subtitle. The image is rendered on top of full overlays only, and is anchored to the top such that viewport resizes do not affect its position.

See the linked bug for example screenshots.

Bug: b/143452916
Change-Id: Ic974b2e0f622edd2a917d1f2fe54b4a4ccd33949
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1886851Reviewed-by: default avatarBrandon Wylie <wylieb@chromium.org>
Reviewed-by: default avatarMathias Carlen <mcarlen@chromium.org>
Commit-Queue: Clemens Arbesser <arbesser@google.com>
Cr-Commit-Position: refs/heads/master@{#711666}
parent 2302c7e1
...@@ -69,6 +69,7 @@ android_library("java") { ...@@ -69,6 +69,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomBarCoordinator.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomSheetContent.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantBottomSheetContent.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDimension.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantKeyboardCoordinator.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantKeyboardCoordinator.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinator.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantOnboardingCoordinator.java",
...@@ -117,6 +118,7 @@ android_library("java") { ...@@ -117,6 +118,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/autofill_assistant/infobox/AssistantInfoBoxViewBinder.java", "java/src/org/chromium/chrome/browser/autofill_assistant/infobox/AssistantInfoBoxViewBinder.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayCoordinator.java", "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayCoordinator.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayDelegate.java", "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayDelegate.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayImage.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java", "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayDrawable.java", "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayDrawable.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayEventFilter.java", "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayEventFilter.java",
...@@ -149,6 +151,7 @@ android_library("java") { ...@@ -149,6 +151,7 @@ android_library("java") {
generate_jni("jni_headers") { generate_jni("jni_headers") {
sources = [ sources = [
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantDimension.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AssistantModel.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java",
......
include_rules = [ include_rules = [
"+chrome/lib/image_fetcher", "+chrome/browser/image_fetcher",
"+chrome/browser/ui/android/widget", "+chrome/browser/ui/android/widget",
"+content/public/android/java/src/org/chromium/content_public/browser", "+content/public/android/java/src/org/chromium/content_public/browser",
"+content/public/android/java/src/org/chromium/content/browser/picker", "+content/public/android/java/src/org/chromium/content/browser/picker",
......
// Copyright 2019 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.chrome.browser.autofill_assistant;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import org.chromium.base.annotations.CalledByNative;
/** Represents a size dimension, such as width or height. */
public abstract class AssistantDimension {
/** Returns the size of this dimension in pixels. */
public abstract int getSizeInPixels(DisplayMetrics displayMetrics);
@CalledByNative
public static AssistantDimension createFromDpi(int dpi) {
return new AssistantDimensionDpi(dpi);
}
@CalledByNative
public static AssistantDimension createFromWidthFactor(float factor) {
return new AssistantDimensionWidthFactor(factor);
}
@CalledByNative
public static AssistantDimension createFromHeightFactor(float factor) {
return new AssistantDimensionHeightFactor(factor);
}
private static class AssistantDimensionDpi extends AssistantDimension {
private final int mDpi;
AssistantDimensionDpi(int dpi) {
mDpi = dpi;
}
@Override
public int getSizeInPixels(DisplayMetrics displayMetrics) {
return Math.round(
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mDpi, displayMetrics));
}
}
private static class AssistantDimensionWidthFactor extends AssistantDimension {
private final double mFactor;
AssistantDimensionWidthFactor(double factor) {
mFactor = factor;
}
@Override
public int getSizeInPixels(DisplayMetrics displayMetrics) {
return (int) Math.round(displayMetrics.widthPixels * mFactor);
}
}
private static class AssistantDimensionHeightFactor extends AssistantDimension {
private final double mFactor;
AssistantDimensionHeightFactor(double factor) {
mFactor = factor;
}
@Override
public int getSizeInPixels(DisplayMetrics displayMetrics) {
return (int) Math.round(displayMetrics.heightPixels * mFactor);
}
}
}
...@@ -4,9 +4,15 @@ ...@@ -4,9 +4,15 @@
package org.chromium.chrome.browser.autofill_assistant.overlay; package org.chromium.chrome.browser.autofill_assistant.overlay;
import android.graphics.Bitmap;
import android.graphics.RectF; import android.graphics.RectF;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.image_fetcher.ImageFetcher;
import org.chromium.chrome.browser.image_fetcher.ImageFetcherConfig;
import org.chromium.chrome.browser.image_fetcher.ImageFetcherFactory;
import org.chromium.chrome.browser.util.AccessibilityUtil; import org.chromium.chrome.browser.util.AccessibilityUtil;
import org.chromium.chrome.browser.widget.ScrimView; import org.chromium.chrome.browser.widget.ScrimView;
import org.chromium.chrome.browser.widget.ScrimView.ScrimParams; import org.chromium.chrome.browser.widget.ScrimView.ScrimParams;
...@@ -23,11 +29,19 @@ public class AssistantOverlayCoordinator { ...@@ -23,11 +29,19 @@ public class AssistantOverlayCoordinator {
private final AssistantOverlayEventFilter mEventFilter; private final AssistantOverlayEventFilter mEventFilter;
private final AssistantOverlayDrawable mDrawable; private final AssistantOverlayDrawable mDrawable;
private final ScrimView mScrim; private final ScrimView mScrim;
private final ImageFetcher mImageFetcher;
private boolean mScrimEnabled; private boolean mScrimEnabled;
public AssistantOverlayCoordinator(ChromeActivity activity, AssistantOverlayModel model) { public AssistantOverlayCoordinator(ChromeActivity activity, AssistantOverlayModel model) {
this(activity, model,
ImageFetcherFactory.createImageFetcher(ImageFetcherConfig.DISK_CACHE_ONLY));
}
public AssistantOverlayCoordinator(
ChromeActivity activity, AssistantOverlayModel model, ImageFetcher imageFetcher) {
mActivity = activity; mActivity = activity;
mModel = model; mModel = model;
mImageFetcher = imageFetcher;
mScrim = mActivity.getScrim(); mScrim = mActivity.getScrim();
mEventFilter = new AssistantOverlayEventFilter( mEventFilter = new AssistantOverlayEventFilter(
mActivity, mActivity.getFullscreenManager(), mActivity.getCompositorViewHolder()); mActivity, mActivity.getFullscreenManager(), mActivity.getCompositorViewHolder());
...@@ -65,6 +79,23 @@ public class AssistantOverlayCoordinator { ...@@ -65,6 +79,23 @@ public class AssistantOverlayCoordinator {
} else if (AssistantOverlayModel.TAP_TRACKING_DURATION_MS == propertyKey) { } else if (AssistantOverlayModel.TAP_TRACKING_DURATION_MS == propertyKey) {
mEventFilter.setTapTrackingDurationMs( mEventFilter.setTapTrackingDurationMs(
model.get(AssistantOverlayModel.TAP_TRACKING_DURATION_MS)); model.get(AssistantOverlayModel.TAP_TRACKING_DURATION_MS));
} else if (AssistantOverlayModel.OVERLAY_IMAGE == propertyKey) {
AssistantOverlayImage image = model.get(AssistantOverlayModel.OVERLAY_IMAGE);
if (image != null && !TextUtils.isEmpty(image.mImageUrl)
&& image.mImageSize != null) {
DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics();
// TODO(b/143517837) Merge autofill assistant image fetcher UMA names.
mImageFetcher.fetchImage(image.mImageUrl,
ImageFetcher.ASSISTANT_DETAILS_UMA_CLIENT_NAME, result -> {
int imageSizePixels =
image.mImageSize.getSizeInPixels(displayMetrics);
image.mImageBitmap = Bitmap.createScaledBitmap(
result, imageSizePixels, imageSizePixels, true);
mDrawable.setFullOverlayImage(image);
});
} else {
mDrawable.setFullOverlayImage(image);
}
} }
}); });
} }
......
...@@ -9,6 +9,8 @@ import android.animation.AnimatorListenerAdapter; ...@@ -9,6 +9,8 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator; import android.animation.TimeInterpolator;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.content.Context; import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.ColorFilter; import android.graphics.ColorFilter;
...@@ -20,11 +22,13 @@ import android.graphics.Rect; ...@@ -20,11 +22,13 @@ import android.graphics.Rect;
import android.graphics.RectF; import android.graphics.RectF;
import android.graphics.Region; import android.graphics.Region;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.support.v4.content.res.ResourcesCompat;
import android.text.TextUtils;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.TypedValue; import android.util.TypedValue;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.chrome.autofill_assistant.R; import org.chromium.chrome.autofill_assistant.R;
...@@ -51,6 +55,9 @@ import java.util.List; ...@@ -51,6 +55,9 @@ import java.util.List;
class AssistantOverlayDrawable extends Drawable implements FullscreenListener { class AssistantOverlayDrawable extends Drawable implements FullscreenListener {
private static final int FADE_DURATION_MS = 250; private static final int FADE_DURATION_MS = 250;
/** '…' in UTF-8. */
private static final String ELLIPSIS = "\u2026";
/** Default background color and alpha. */ /** Default background color and alpha. */
private static final int DEFAULT_BACKGROUND_COLOR = Color.argb(0x42, 0, 0, 0); private static final int DEFAULT_BACKGROUND_COLOR = Color.argb(0x42, 0, 0, 0);
...@@ -72,6 +79,7 @@ class AssistantOverlayDrawable extends Drawable implements FullscreenListener { ...@@ -72,6 +79,7 @@ class AssistantOverlayDrawable extends Drawable implements FullscreenListener {
private int mBoxStrokeAlpha; private int mBoxStrokeAlpha;
private final Paint mBoxClear; private final Paint mBoxClear;
private final Paint mBoxFill; private final Paint mBoxFill;
private final Paint mTextPaint;
/** When in partial mode, don't draw on {@link #mTransparentArea}. */ /** When in partial mode, don't draw on {@link #mTransparentArea}. */
private boolean mPartial; private boolean mPartial;
...@@ -99,6 +107,9 @@ class AssistantOverlayDrawable extends Drawable implements FullscreenListener { ...@@ -99,6 +107,9 @@ class AssistantOverlayDrawable extends Drawable implements FullscreenListener {
/** A single RectF instance used for drawing, to avoid creating many instances when drawing. */ /** A single RectF instance used for drawing, to avoid creating many instances when drawing. */
private final RectF mDrawRect = new RectF(); private final RectF mDrawRect = new RectF();
/** The image to draw on top of full overlays, if set. */
private AssistantOverlayImage mOverlayImage;
private AssistantOverlayDelegate mDelegate; private AssistantOverlayDelegate mDelegate;
AssistantOverlayDrawable(Context context, ChromeFullscreenManager fullscreenManager) { AssistantOverlayDrawable(Context context, ChromeFullscreenManager fullscreenManager) {
...@@ -131,6 +142,21 @@ class AssistantOverlayDrawable extends Drawable implements FullscreenListener { ...@@ -131,6 +142,21 @@ class AssistantOverlayDrawable extends Drawable implements FullscreenListener {
mFullscreenManager.addListener(this); mFullscreenManager.addListener(this);
// Inherit font from AssistantBlackBody style.
mTextPaint = new Paint();
TypedArray styled_attributes = context.obtainStyledAttributes(
R.style.TextAppearance_AssistantBlackBody, R.styleable.TextAppearance);
if (styled_attributes.hasValue(R.styleable.TextAppearance_android_fontFamily)) {
int fontId = styled_attributes.getResourceId(
R.styleable.TextAppearance_android_fontFamily, -1);
try {
mTextPaint.setTypeface(ResourcesCompat.getFont(context, fontId));
} catch (Resources.NotFoundException ignored) {
// Use default font
}
}
styled_attributes.recycle();
// Sets colors to default. // Sets colors to default.
setBackgroundColor(null); setBackgroundColor(null);
setHighlightBorderColor(null); setHighlightBorderColor(null);
...@@ -230,6 +256,21 @@ class AssistantOverlayDrawable extends Drawable implements FullscreenListener { ...@@ -230,6 +256,21 @@ class AssistantOverlayDrawable extends Drawable implements FullscreenListener {
invalidateSelf(); invalidateSelf();
} }
void setFullOverlayImage(@Nullable AssistantOverlayImage overlayImage) {
mOverlayImage = overlayImage;
if (mOverlayImage == null) {
invalidateSelf();
return;
}
if (mOverlayImage.mTextSize != null) {
mTextPaint.setTextSize(mOverlayImage.mTextSize.getSizeInPixels(
mContext.getResources().getDisplayMetrics()));
}
mTextPaint.setColor(mOverlayImage.mTextColor);
invalidateSelf();
}
/** Set or update the restricted area. */ /** Set or update the restricted area. */
void setRestrictedArea(List<RectF> restrictedArea) { void setRestrictedArea(List<RectF> restrictedArea) {
mRestrictedArea = restrictedArea; mRestrictedArea = restrictedArea;
...@@ -254,6 +295,7 @@ class AssistantOverlayDrawable extends Drawable implements FullscreenListener { ...@@ -254,6 +295,7 @@ class AssistantOverlayDrawable extends Drawable implements FullscreenListener {
public void draw(Canvas canvas) { public void draw(Canvas canvas) {
Rect bounds = getBounds(); Rect bounds = getBounds();
int width = bounds.width(); int width = bounds.width();
int yTop = mFullscreenManager.getContentOffset();
int yBottom = bounds.height() - mFullscreenManager.getBottomControlsHeight() int yBottom = bounds.height() - mFullscreenManager.getBottomControlsHeight()
- mFullscreenManager.getBottomControlOffset(); - mFullscreenManager.getBottomControlOffset();
...@@ -262,6 +304,33 @@ class AssistantOverlayDrawable extends Drawable implements FullscreenListener { ...@@ -262,6 +304,33 @@ class AssistantOverlayDrawable extends Drawable implements FullscreenListener {
canvas.drawPaint(mBackground); canvas.drawPaint(mBackground);
// Draw overlay image, if specified.
if (mTransparentArea.isEmpty() && mOverlayImage != null
&& mOverlayImage.mImageBitmap != null && mOverlayImage.mImageSize != null) {
DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
int imageSize = mOverlayImage.mImageSize.getSizeInPixels(displayMetrics);
int topMargin = 0;
if (mOverlayImage.mImageTopMargin != null) {
topMargin = mOverlayImage.mImageTopMargin.getSizeInPixels(displayMetrics);
}
canvas.drawBitmap(mOverlayImage.mImageBitmap,
bounds.left + (bounds.right - bounds.left) / 2.0f - imageSize / 2.0f,
yTop + topMargin, null);
if (!TextUtils.isEmpty(mOverlayImage.mText)) {
int bottomMargin = 0;
if (mOverlayImage.mImageBottomMargin != null) {
bottomMargin = mOverlayImage.mImageBottomMargin.getSizeInPixels(displayMetrics);
}
String text = trimStringToWidth(
mOverlayImage.mText, bounds.right - bounds.left, mTextPaint);
float textWidth = mTextPaint.measureText(text);
canvas.drawText(text,
bounds.left + (bounds.right - bounds.left) / 2.0f - textWidth / 2.0f,
yTop + topMargin + imageSize + bottomMargin, mTextPaint);
}
}
if (mVisualViewport.isEmpty()) return; if (mVisualViewport.isEmpty()) return;
// Ratio of to use to convert zoomed CSS pixels, to physical pixels. Aspect ratio is // Ratio of to use to convert zoomed CSS pixels, to physical pixels. Aspect ratio is
...@@ -269,8 +338,6 @@ class AssistantOverlayDrawable extends Drawable implements FullscreenListener { ...@@ -269,8 +338,6 @@ class AssistantOverlayDrawable extends Drawable implements FullscreenListener {
// here, since viewport width always corresponds to the overlay width. // here, since viewport width always corresponds to the overlay width.
float cssPixelsToPhysical = ((float) width) / mVisualViewport.width(); float cssPixelsToPhysical = ((float) width) / mVisualViewport.width();
int yTop = mFullscreenManager.getContentOffset();
// Don't draw on top of the restricted area. // Don't draw on top of the restricted area.
for (RectF rect : mRestrictedArea) { for (RectF rect : mRestrictedArea) {
mDrawRect.left = (rect.left - mVisualViewport.left) * cssPixelsToPhysical; mDrawRect.left = (rect.left - mVisualViewport.left) * cssPixelsToPhysical;
...@@ -341,6 +408,24 @@ class AssistantOverlayDrawable extends Drawable implements FullscreenListener { ...@@ -341,6 +408,24 @@ class AssistantOverlayDrawable extends Drawable implements FullscreenListener {
} }
} }
/**
* Trims {@code text} until its width is smaller or equal {@code width} when rendered with
* {@code paint}. If characters are removed, an ellipsis ('…') is appended.
* @return the trimmed string, possibly with a trailing ellipsis.
*/
private String trimStringToWidth(String text, int width, Paint paint) {
String trimmedText = text;
float textWidth = paint.measureText(trimmedText);
if (textWidth > width) {
while (!TextUtils.isEmpty(trimmedText) && textWidth > width) {
trimmedText = trimmedText.substring(0, trimmedText.length() - 1);
textWidth = paint.measureText(trimmedText + ELLIPSIS);
}
trimmedText = trimmedText + ELLIPSIS;
}
return trimmedText;
}
@IntDef({AnimationType.NONE, AnimationType.FADE_IN, AnimationType.FADE_OUT}) @IntDef({AnimationType.NONE, AnimationType.FADE_IN, AnimationType.FADE_OUT})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
private @interface AnimationType { private @interface AnimationType {
......
// Copyright 2019 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.chrome.browser.autofill_assistant.overlay;
import android.graphics.Bitmap;
import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import org.chromium.chrome.browser.autofill_assistant.AssistantDimension;
/** Java equivalent to {@code OverlayImageProto}. */
public class AssistantOverlayImage {
/** The url of the image to display. */
public final String mImageUrl;
/** The image to display, after {@code mImageUrl} has been resolved. */
public @Nullable Bitmap mImageBitmap;
/** The size of the image to display. */
public final @Nullable AssistantDimension mImageSize;
/** The margin between the top of the page (anchor) and the image. */
public final @Nullable AssistantDimension mImageTopMargin;
/** The margin between image and text. */
public final @Nullable AssistantDimension mImageBottomMargin;
/** The text to display beneath the image. */
public final String mText;
/** The color of the text to draw */
public final @ColorInt int mTextColor;
/** The size of the text to display. */
public final @Nullable AssistantDimension mTextSize;
public AssistantOverlayImage(String imageUrl, @Nullable AssistantDimension imageSize,
@Nullable AssistantDimension imageTopMargin,
@Nullable AssistantDimension imageBottomMargin, String text, @ColorInt int textColor,
@Nullable AssistantDimension textSize) {
mImageUrl = imageUrl;
mImageSize = imageSize;
mImageTopMargin = imageTopMargin;
mImageBottomMargin = imageBottomMargin;
mText = text;
mTextColor = textColor;
mTextSize = textSize;
}
}
...@@ -6,11 +6,13 @@ package org.chromium.chrome.browser.autofill_assistant.overlay; ...@@ -6,11 +6,13 @@ package org.chromium.chrome.browser.autofill_assistant.overlay;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.RectF; import android.graphics.RectF;
import android.support.annotation.Nullable;
import androidx.annotation.ColorInt; import androidx.annotation.ColorInt;
import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.JNINamespace;
import org.chromium.chrome.browser.autofill_assistant.AssistantDimension;
import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -47,9 +49,13 @@ public class AssistantOverlayModel extends PropertyModel { ...@@ -47,9 +49,13 @@ public class AssistantOverlayModel extends PropertyModel {
public static final WritableObjectPropertyKey<Long> TAP_TRACKING_DURATION_MS = public static final WritableObjectPropertyKey<Long> TAP_TRACKING_DURATION_MS =
new WritableObjectPropertyKey<>(); new WritableObjectPropertyKey<>();
public static final WritableObjectPropertyKey<AssistantOverlayImage> OVERLAY_IMAGE =
new WritableObjectPropertyKey<>();
public AssistantOverlayModel() { public AssistantOverlayModel() {
super(STATE, TOUCHABLE_AREA, RESTRICTED_AREA, VISUAL_VIEWPORT, DELEGATE, BACKGROUND_COLOR, super(STATE, TOUCHABLE_AREA, RESTRICTED_AREA, VISUAL_VIEWPORT, DELEGATE, BACKGROUND_COLOR,
HIGHLIGHT_BORDER_COLOR, TAP_TRACKING_COUNT, TAP_TRACKING_DURATION_MS); HIGHLIGHT_BORDER_COLOR, TAP_TRACKING_COUNT, TAP_TRACKING_DURATION_MS,
OVERLAY_IMAGE);
} }
@CalledByNative @CalledByNative
...@@ -106,6 +112,21 @@ public class AssistantOverlayModel extends PropertyModel { ...@@ -106,6 +112,21 @@ public class AssistantOverlayModel extends PropertyModel {
set(HIGHLIGHT_BORDER_COLOR, null); set(HIGHLIGHT_BORDER_COLOR, null);
} }
@CalledByNative
private void setOverlayImage(String imageUrl, @Nullable AssistantDimension imageSize,
@Nullable AssistantDimension imageTopMargin,
@Nullable AssistantDimension imageBottomMargin, String text, @ColorInt int textColor,
@Nullable AssistantDimension textSize) {
set(OVERLAY_IMAGE,
new AssistantOverlayImage(imageUrl, imageSize, imageTopMargin, imageBottomMargin,
text, textColor, textSize));
}
@CalledByNative
private void clearOverlayImage() {
set(OVERLAY_IMAGE, null);
}
/** /**
* Parses {@code colorString} and returns the corresponding color integer. This is only safe to * Parses {@code colorString} and returns the corresponding color integer. This is only safe to
* call for valid strings, which should be checked with {@code isValidColorString} before * call for valid strings, which should be checked with {@code isValidColorString} before
......
...@@ -15,6 +15,9 @@ import static org.hamcrest.Matchers.not; ...@@ -15,6 +15,9 @@ import static org.hamcrest.Matchers.not;
import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking; import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.RectF; import android.graphics.RectF;
import android.support.test.InstrumentationRegistry; import android.support.test.InstrumentationRegistry;
...@@ -30,6 +33,7 @@ import org.junit.runner.RunWith; ...@@ -30,6 +33,7 @@ import org.junit.runner.RunWith;
import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator; import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayImage;
import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayModel; import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayModel;
import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayState; import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayState;
import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule; import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
...@@ -70,8 +74,13 @@ public class AutofillAssistantOverlayUiTest { ...@@ -70,8 +74,13 @@ public class AutofillAssistantOverlayUiTest {
/** Creates a coordinator for use in UI tests. */ /** Creates a coordinator for use in UI tests. */
private AssistantOverlayCoordinator createCoordinator(AssistantOverlayModel model) private AssistantOverlayCoordinator createCoordinator(AssistantOverlayModel model)
throws ExecutionException { throws ExecutionException {
Bitmap testImage = BitmapFactory.decodeResource(mTestRule.getActivity().getResources(),
org.chromium.chrome.autofill_assistant.R.drawable.btn_close);
return runOnUiThreadBlocking( return runOnUiThreadBlocking(
() -> new AssistantOverlayCoordinator(mTestRule.getActivity(), model)); ()
-> new AssistantOverlayCoordinator(mTestRule.getActivity(), model,
new AutofillAssistantUiTestUtil.MockImageFetcher(testImage, null)));
} }
/** Tests assumptions about the initial state of the infobox. */ /** Tests assumptions about the initial state of the infobox. */
...@@ -106,6 +115,25 @@ public class AutofillAssistantOverlayUiTest { ...@@ -106,6 +115,25 @@ public class AutofillAssistantOverlayUiTest {
assertThat(checkElementExists("touch_area_one"), is(false)); assertThat(checkElementExists("touch_area_one"), is(false));
} }
/** Tests assumptions about the full overlay. */
@Test
@MediumTest
public void testFullOverlayWithImage() throws Exception {
AssistantOverlayModel model = new AssistantOverlayModel();
AssistantOverlayCoordinator coordinator = createCoordinator(model);
AssistantOverlayImage image = new AssistantOverlayImage("http://localhost/example.png",
AssistantDimension.createFromDpi(24), AssistantDimension.createFromDpi(24),
AssistantDimension.createFromDpi(20), "example.com", Color.parseColor("#B3FFFFFF"),
AssistantDimension.createFromDpi(14));
runOnUiThreadBlocking(() -> {
model.set(AssistantOverlayModel.STATE, AssistantOverlayState.FULL);
model.set(AssistantOverlayModel.OVERLAY_IMAGE, image);
});
assertScrimDisplayed(true);
// TODO(b/143452916): Test that the overlay image is actually being displayed.
}
/** Tests assumptions about the partial overlay. */ /** Tests assumptions about the partial overlay. */
@Test @Test
@MediumTest @MediumTest
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "chrome/android/features/autofill_assistant/jni_headers/AssistantCollectUserDataModel_jni.h" #include "chrome/android/features/autofill_assistant/jni_headers/AssistantCollectUserDataModel_jni.h"
#include "chrome/android/features/autofill_assistant/jni_headers/AssistantDetailsModel_jni.h" #include "chrome/android/features/autofill_assistant/jni_headers/AssistantDetailsModel_jni.h"
#include "chrome/android/features/autofill_assistant/jni_headers/AssistantDetails_jni.h" #include "chrome/android/features/autofill_assistant/jni_headers/AssistantDetails_jni.h"
#include "chrome/android/features/autofill_assistant/jni_headers/AssistantDimension_jni.h"
#include "chrome/android/features/autofill_assistant/jni_headers/AssistantFormInput_jni.h" #include "chrome/android/features/autofill_assistant/jni_headers/AssistantFormInput_jni.h"
#include "chrome/android/features/autofill_assistant/jni_headers/AssistantFormModel_jni.h" #include "chrome/android/features/autofill_assistant/jni_headers/AssistantFormModel_jni.h"
#include "chrome/android/features/autofill_assistant/jni_headers/AssistantHeaderModel_jni.h" #include "chrome/android/features/autofill_assistant/jni_headers/AssistantHeaderModel_jni.h"
...@@ -79,6 +80,26 @@ base::android::ScopedJavaLocalRef<jobject> CreateJavaDateTime( ...@@ -79,6 +80,26 @@ base::android::ScopedJavaLocalRef<jobject> CreateJavaDateTime(
proto.time().hour(), proto.time().minute(), proto.time().second()); proto.time().hour(), proto.time().minute(), proto.time().second());
} }
base::android::ScopedJavaLocalRef<jobject> CreateJavaClientDimension(
JNIEnv* env,
const ClientDimensionProto& proto) {
switch (proto.size_case()) {
case ClientDimensionProto::kDpi:
return Java_AssistantDimension_createFromDpi(env, proto.dpi());
break;
case ClientDimensionProto::kWidthFactor:
return Java_AssistantDimension_createFromWidthFactor(
env, proto.width_factor());
break;
case ClientDimensionProto::kHeightFactor:
return Java_AssistantDimension_createFromHeightFactor(
env, proto.height_factor());
break;
case ClientDimensionProto::SIZE_NOT_SET:
return nullptr;
}
}
// Returns a 32-bit integer representing |color_string| in Java. Uses // Returns a 32-bit integer representing |color_string| in Java. Uses
// base::Optional to distinguish between valid and invalid colors. // base::Optional to distinguish between valid and invalid colors.
base::Optional<int> CreateJavaColor(JNIEnv* env, base::Optional<int> CreateJavaColor(JNIEnv* env,
...@@ -1029,9 +1050,31 @@ void UiControllerAndroid::OnFormChanged(const FormProto* form) { ...@@ -1029,9 +1050,31 @@ void UiControllerAndroid::OnFormChanged(const FormProto* form) {
void UiControllerAndroid::OnClientSettingsChanged( void UiControllerAndroid::OnClientSettingsChanged(
const ClientSettings& settings) { const ClientSettings& settings) {
JNIEnv* env = AttachCurrentThread();
Java_AssistantOverlayModel_setTapTracking( Java_AssistantOverlayModel_setTapTracking(
AttachCurrentThread(), GetOverlayModel(), settings.tap_count, env, GetOverlayModel(), settings.tap_count,
settings.tap_tracking_duration.InMilliseconds()); settings.tap_tracking_duration.InMilliseconds());
if (settings.overlay_image.has_value()) {
const auto& image = *(settings.overlay_image);
auto text_color = CreateJavaColor(env, image.text_color());
if (!text_color.has_value()) {
DVLOG(1) << __func__ << "Invalid text color for overlay image: "
<< image.text_color();
Java_AssistantOverlayModel_clearOverlayImage(env, GetOverlayModel());
} else {
Java_AssistantOverlayModel_setOverlayImage(
env, GetOverlayModel(),
base::android::ConvertUTF8ToJavaString(env, image.image_url()),
CreateJavaClientDimension(env, image.image_size()),
CreateJavaClientDimension(env, image.image_top_margin()),
CreateJavaClientDimension(env, image.image_bottom_margin()),
base::android::ConvertUTF8ToJavaString(env, image.text()),
*text_color, CreateJavaClientDimension(env, image.text_size()));
}
} else {
Java_AssistantOverlayModel_clearOverlayImage(env, GetOverlayModel());
}
} }
void UiControllerAndroid::OnCounterChanged(int input_index, void UiControllerAndroid::OnCounterChanged(int input_index,
......
...@@ -4,11 +4,30 @@ ...@@ -4,11 +4,30 @@
#include "components/autofill_assistant/browser/client_settings.h" #include "components/autofill_assistant/browser/client_settings.h"
#include "components/autofill_assistant/browser/service.pb.h" namespace {
bool IsValidOverlayImageProto(
const autofill_assistant::OverlayImageProto& proto) {
if (!proto.image_url().empty() && !proto.has_image_size()) {
DVLOG(1) << __func__ << ": Missing image_size in overlay_image, ignoring";
return false;
}
if (!proto.text().empty() &&
(!proto.has_text_color() || !proto.has_text_size())) {
DVLOG(1) << __func__
<< ": Missing text_color or text_size in overlay_image, ignoring";
return false;
}
return true;
}
} // namespace
namespace autofill_assistant { namespace autofill_assistant {
ClientSettings::ClientSettings() = default; ClientSettings::ClientSettings() = default;
ClientSettings::~ClientSettings() = default;
void ClientSettings::UpdateFromProto(const ClientSettingsProto& proto) { void ClientSettings::UpdateFromProto(const ClientSettingsProto& proto) {
if (proto.has_periodic_script_check_interval_ms()) { if (proto.has_periodic_script_check_interval_ms()) {
...@@ -58,6 +77,12 @@ void ClientSettings::UpdateFromProto(const ClientSettingsProto& proto) { ...@@ -58,6 +77,12 @@ void ClientSettings::UpdateFromProto(const ClientSettingsProto& proto) {
tap_shutdown_delay = tap_shutdown_delay =
base::TimeDelta::FromMilliseconds(proto.tap_shutdown_delay_ms()); base::TimeDelta::FromMilliseconds(proto.tap_shutdown_delay_ms());
} }
if (proto.has_overlay_image() &&
IsValidOverlayImageProto(proto.overlay_image())) {
overlay_image = proto.overlay_image();
} else {
overlay_image.reset();
}
} }
} // namespace autofill_assistant } // namespace autofill_assistant
...@@ -6,10 +6,11 @@ ...@@ -6,10 +6,11 @@
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_CLIENT_SETTINGS_H_ #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_CLIENT_SETTINGS_H_
#include "base/macros.h" #include "base/macros.h"
#include "base/optional.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "components/autofill_assistant/browser/service.pb.h"
namespace autofill_assistant { namespace autofill_assistant {
class ClientSettingsProto;
// Global settings for the Autofill Assistant client. // Global settings for the Autofill Assistant client.
// //
...@@ -20,6 +21,7 @@ class ClientSettingsProto; ...@@ -20,6 +21,7 @@ class ClientSettingsProto;
// pointer to the single ClientSettings instance instead of making a copy. // pointer to the single ClientSettings instance instead of making a copy.
struct ClientSettings { struct ClientSettings {
ClientSettings(); ClientSettings();
~ClientSettings();
// Time between two periodic script precondition checks. // Time between two periodic script precondition checks.
base::TimeDelta periodic_script_check_interval = base::TimeDelta periodic_script_check_interval =
...@@ -76,6 +78,9 @@ struct ClientSettings { ...@@ -76,6 +78,9 @@ struct ClientSettings {
// taps where // taps where
base::TimeDelta tap_shutdown_delay = base::TimeDelta::FromSeconds(5); base::TimeDelta tap_shutdown_delay = base::TimeDelta::FromSeconds(5);
// Optional image drawn on top of overlays.
base::Optional<OverlayImageProto> overlay_image;
void UpdateFromProto(const ClientSettingsProto& proto); void UpdateFromProto(const ClientSettingsProto& proto);
private: private:
......
...@@ -76,6 +76,36 @@ message SupportsScriptResponseProto { ...@@ -76,6 +76,36 @@ message SupportsScriptResponseProto {
optional ClientSettingsProto client_settings = 3; optional ClientSettingsProto client_settings = 3;
} }
// Represents a dimension, e.g., width or height.
message ClientDimensionProto {
oneof size {
int32 dpi = 1;
// Factor to multiply with the client's total width.
float width_factor = 2;
// Factor to multiply with the client's total height.
float height_factor = 3;
}
}
// Overlay image to be drawn on top of full overlays.
message OverlayImageProto {
// The image to display. If set, |image_size| is mandatory.
optional string image_url = 1;
// The size of the image to display.
optional ClientDimensionProto image_size = 2;
// The margin between the top of the page (anchor) and the image.
optional ClientDimensionProto image_top_margin = 3;
// The margin between the bottom of the image and the baseline of the text.
optional ClientDimensionProto image_bottom_margin = 4;
// The text to display beneath the image. If set, |text_color| and |text_size|
// are mandatory.
optional string text = 5;
// Supported formats: #RRGGBB or #AARRGGBB.
optional string text_color = 6;
// The size of the text to display.
optional ClientDimensionProto text_size = 7;
}
message ClientSettingsProto { message ClientSettingsProto {
reserved 10, 11; reserved 10, 11;
...@@ -127,6 +157,9 @@ message ClientSettingsProto { ...@@ -127,6 +157,9 @@ message ClientSettingsProto {
// How much time to give users to tap undo when after |tap_count| unexpected // How much time to give users to tap undo when after |tap_count| unexpected
// taps where // taps where
optional int32 tap_shutdown_delay_ms = 15; optional int32 tap_shutdown_delay_ms = 15;
// Optional image drawn on top of overlays.
optional OverlayImageProto overlay_image = 16;
} }
message ScriptTimeoutError { message ScriptTimeoutError {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment