| Index: components/autofill/android/java/src/org/chromium/components/autofill/AutofillKeyboardAccessory.java
|
| diff --git a/components/autofill/android/java/src/org/chromium/components/autofill/AutofillKeyboardAccessory.java b/components/autofill/android/java/src/org/chromium/components/autofill/AutofillKeyboardAccessory.java
|
| index 56475a385245b4eda771492457202070f1e3d3b9..b36b9f5b698fc6175a25ac93502e82980b4e591a 100644
|
| --- a/components/autofill/android/java/src/org/chromium/components/autofill/AutofillKeyboardAccessory.java
|
| +++ b/components/autofill/android/java/src/org/chromium/components/autofill/AutofillKeyboardAccessory.java
|
| @@ -4,8 +4,13 @@
|
|
|
| package org.chromium.components.autofill;
|
|
|
| +import android.animation.Animator;
|
| +import android.animation.AnimatorListenerAdapter;
|
| +import android.animation.ObjectAnimator;
|
| import android.annotation.SuppressLint;
|
| +import android.graphics.PorterDuff;
|
| import android.graphics.Typeface;
|
| +import android.graphics.drawable.Drawable;
|
| import android.os.Build;
|
| import android.support.v7.content.res.AppCompatResources;
|
| import android.text.TextUtils;
|
| @@ -29,19 +34,32 @@ import org.chromium.ui.base.WindowAndroid;
|
| public class AutofillKeyboardAccessory extends LinearLayout
|
| implements WindowAndroid.KeyboardVisibilityListener, View.OnClickListener,
|
| View.OnLongClickListener {
|
| + // Time to pause before reversing animation when the first suggestion is a hint.
|
| + private static final long PAUSE_ANIMATION_BEFORE_REVERSE_MILLIS = 1000;
|
| +
|
| private final WindowAndroid mWindowAndroid;
|
| private final AutofillDelegate mAutofillDelegate;
|
| + // If |mMaximumLabelWidthPx| is 0, we do not call |setMaxWidth| on the |TextView| for a fillable
|
| + // suggestion label.
|
| private final int mMaximumLabelWidthPx;
|
| private final int mMaximumSublabelWidthPx;
|
| + private final int mAnimationDurationMillis;
|
| + // We start animating by scrolling the suggestions from beyond the viewport.
|
| + private final int mStartAnimationTranslationPx;
|
| +
|
| + private ObjectAnimator mAnimator;
|
| + private Runnable mReverseAnimationRunnable;
|
|
|
| /**
|
| * Creates an AutofillKeyboardAccessory with specified parameters.
|
| * @param windowAndroid The owning WindowAndroid.
|
| * @param autofillDelegate A object that handles the calls to the native
|
| * AutofillKeyboardAccessoryView.
|
| + * @param animationDurationMillis If 0, do not animate.
|
| + * @param shouldLimitLabelWidth If true, limit suggestion label width to 1/2 device's width.
|
| */
|
| - public AutofillKeyboardAccessory(
|
| - WindowAndroid windowAndroid, AutofillDelegate autofillDelegate) {
|
| + public AutofillKeyboardAccessory(WindowAndroid windowAndroid, AutofillDelegate autofillDelegate,
|
| + int animationDurationMillis, boolean shouldLimitLabelWidth) {
|
| super(windowAndroid.getActivity().get());
|
| assert autofillDelegate != null;
|
| assert windowAndroid.getActivity().get() != null;
|
| @@ -49,13 +67,17 @@ public class AutofillKeyboardAccessory extends LinearLayout
|
| mAutofillDelegate = autofillDelegate;
|
|
|
| int deviceWidthPx = windowAndroid.getDisplay().getDisplayWidth();
|
| - mMaximumLabelWidthPx = deviceWidthPx / 2;
|
| + mMaximumLabelWidthPx = shouldLimitLabelWidth ? deviceWidthPx / 2 : 0;
|
| mMaximumSublabelWidthPx = deviceWidthPx / 4;
|
|
|
| mWindowAndroid.addKeyboardVisibilityListener(this);
|
| int horizontalPaddingPx = getResources().getDimensionPixelSize(
|
| R.dimen.keyboard_accessory_half_padding);
|
| setPadding(horizontalPaddingPx, 0, horizontalPaddingPx, 0);
|
| +
|
| + mAnimationDurationMillis = animationDurationMillis;
|
| + mStartAnimationTranslationPx = getResources().getDimensionPixelSize(
|
| + R.dimen.keyboard_accessory_start_animation_translation);
|
| }
|
|
|
| /**
|
| @@ -65,23 +87,42 @@ public class AutofillKeyboardAccessory extends LinearLayout
|
| */
|
| @SuppressLint("InlinedApi")
|
| public void showWithSuggestions(AutofillSuggestion[] suggestions, final boolean isRtl) {
|
| + assert suggestions.length > 0;
|
| + // The first suggestion may be a hint to call attention to the keyboard accessory. See
|
| + // |IsHintEnabledInKeyboardAccessory|. A 'hint' suggestion does not have a label and is not
|
| + // fillable, but has an icon.
|
| + final boolean isFirstSuggestionAHint = TextUtils.isEmpty(suggestions[0].getLabel());
|
| + if (isFirstSuggestionAHint) {
|
| + assert suggestions[0].getIconId() != 0 && !suggestions[0].isFillable();
|
| + }
|
| +
|
| removeAllViews();
|
| int separatorPosition = -1;
|
| for (int i = 0; i < suggestions.length; i++) {
|
| AutofillSuggestion suggestion = suggestions[i];
|
| - assert !TextUtils.isEmpty(suggestion.getLabel());
|
| + boolean isKeyboardAccessoryHint = i == 0 && isFirstSuggestionAHint;
|
| + if (!isKeyboardAccessoryHint) {
|
| + assert !TextUtils.isEmpty(suggestion.getLabel());
|
| + }
|
|
|
| View touchTarget;
|
| if (!suggestion.isFillable() && suggestion.getIconId() != 0) {
|
| touchTarget = LayoutInflater.from(getContext()).inflate(
|
| R.layout.autofill_keyboard_accessory_icon, this, false);
|
|
|
| - if (separatorPosition == -1) separatorPosition = i;
|
| + if (separatorPosition == -1 && !isKeyboardAccessoryHint) separatorPosition = i;
|
|
|
| ImageView icon = (ImageView) touchTarget;
|
| - icon.setImageDrawable(
|
| - AppCompatResources.getDrawable(getContext(), suggestion.getIconId()));
|
| - icon.setContentDescription(suggestion.getLabel());
|
| + Drawable drawable =
|
| + AppCompatResources.getDrawable(getContext(), suggestion.getIconId());
|
| + if (isKeyboardAccessoryHint) {
|
| + drawable.setColorFilter(ApiCompatibilityUtils.getColor(getResources(),
|
| + R.color.keyboard_accessory_hint_icon),
|
| + PorterDuff.Mode.SRC_IN);
|
| + } else {
|
| + icon.setContentDescription(suggestion.getLabel());
|
| + }
|
| + icon.setImageDrawable(drawable);
|
| } else {
|
| touchTarget = LayoutInflater.from(getContext()).inflate(
|
| R.layout.autofill_keyboard_accessory_item, this, false);
|
| @@ -89,7 +130,7 @@ public class AutofillKeyboardAccessory extends LinearLayout
|
| TextView label = (TextView) touchTarget.findViewById(
|
| R.id.autofill_keyboard_accessory_item_label);
|
|
|
| - if (suggestion.isFillable()) {
|
| + if (mMaximumLabelWidthPx > 0 && suggestion.isFillable()) {
|
| label.setMaxWidth(mMaximumLabelWidthPx);
|
| }
|
|
|
| @@ -115,36 +156,63 @@ public class AutofillKeyboardAccessory extends LinearLayout
|
| }
|
| }
|
|
|
| - touchTarget.setTag(i);
|
| - touchTarget.setOnClickListener(this);
|
| - if (suggestion.isDeletable()) {
|
| - touchTarget.setOnLongClickListener(this);
|
| + if (!isKeyboardAccessoryHint) {
|
| + touchTarget.setTag(i);
|
| + touchTarget.setOnClickListener(this);
|
| + if (suggestion.isDeletable()) {
|
| + touchTarget.setOnLongClickListener(this);
|
| + }
|
| }
|
| -
|
| addView(touchTarget);
|
| }
|
|
|
| if (separatorPosition != -1) {
|
| - View separator = new View(getContext());
|
| - separator.setLayoutParams(new LinearLayout.LayoutParams(0, 0, 1));
|
| - addView(separator, separatorPosition);
|
| + addView(createSeparatorView(), separatorPosition);
|
| }
|
|
|
| + // TODO(crbug/722897): Following does not reverse layout order for RTL.
|
| ApiCompatibilityUtils.setLayoutDirection(
|
| this, isRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
|
|
|
| final HorizontalScrollView container =
|
| (HorizontalScrollView) mWindowAndroid.getKeyboardAccessoryView();
|
| +
|
| if (getParent() == null) {
|
| container.addView(this);
|
| - container.setVisibility(View.VISIBLE);
|
| + // If we are animating the view, we |setVisibility| in |onAnimationStart|.
|
| + if (mAnimationDurationMillis == 0) {
|
| + container.setVisibility(View.VISIBLE);
|
| + }
|
| container.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
|
| }
|
|
|
| + if (mAnimationDurationMillis > 0) {
|
| + cancelAnimations(container);
|
| + mAnimator = ObjectAnimator.ofFloat(
|
| + this, View.TRANSLATION_X, -mStartAnimationTranslationPx, 0);
|
| + mAnimator.setDuration(mAnimationDurationMillis);
|
| + mAnimator.addListener(new AnimatorListenerAdapter() {
|
| + @Override
|
| + public void onAnimationStart(Animator animator) {
|
| + container.setVisibility(View.VISIBLE);
|
| + }
|
| + @Override
|
| + public void onAnimationEnd(Animator animator) {
|
| + mAnimator.removeListener(this);
|
| + if (isFirstSuggestionAHint && container.getVisibility() == View.VISIBLE) {
|
| + scheduleReverseAnimation(container);
|
| + }
|
| + }
|
| + });
|
| + }
|
| container.post(new Runnable() {
|
| @Override
|
| public void run() {
|
| - container.scrollTo(isRtl ? getRight() : 0, 0);
|
| + if (mAnimationDurationMillis > 0) {
|
| + mAnimator.start();
|
| + } else {
|
| + container.scrollTo(isRtl ? getRight() : 0, 0);
|
| + }
|
| }
|
| });
|
| }
|
| @@ -179,4 +247,56 @@ public class AutofillKeyboardAccessory extends LinearLayout
|
| mAutofillDelegate.deleteSuggestion((int) v.getTag());
|
| return true;
|
| }
|
| +
|
| + // Helper to create separator view so that the settings icon is aligned to the right of the
|
| + // screen.
|
| + private View createSeparatorView() {
|
| + View separator = new View(getContext());
|
| + // Specify a layout weight so that the settings icon, which is displayed after the
|
| + // separator, is aligned with the edge of the viewport.
|
| + separator.setLayoutParams(new LinearLayout.LayoutParams(0, 0, 1));
|
| + return separator;
|
| + }
|
| +
|
| + // Cancels any ongoing and pending reverse animations.
|
| + private void cancelAnimations(View view) {
|
| + if (mAnimator != null && mAnimator.isStarted()) mAnimator.cancel();
|
| + view.removeCallbacks(mReverseAnimationRunnable);
|
| + mAnimator = null;
|
| + }
|
| +
|
| + // Schedules the reverse of the animation. Scrolls the first suggestion (which is a non-fillable
|
| + // hint) out of the viewport at the end of the reversed animation.
|
| + private void scheduleReverseAnimation(final View view) {
|
| + assert getChildCount() > 1;
|
| + // We may have removed the keyboardAccessoryHint from a previous animation that we
|
| + // cancelled.
|
| + final View firstSuggestion = getChildAt(0);
|
| + if (!(firstSuggestion instanceof ImageView
|
| + && firstSuggestion.getContentDescription() == null)) {
|
| + return;
|
| + }
|
| +
|
| + int hintWidth = firstSuggestion.getWidth() - firstSuggestion.getPaddingRight();
|
| + mAnimator = ObjectAnimator.ofFloat(this, View.TRANSLATION_X, 0, -hintWidth);
|
| + mAnimator.setDuration(mAnimationDurationMillis);
|
| + mAnimator.addListener(new AnimatorListenerAdapter() {
|
| + @Override
|
| + public void onAnimationEnd(Animator animator) {
|
| + mAnimator.removeListener(this);
|
| + if (view.getVisibility() == View.VISIBLE) {
|
| + // Remove the keyboard accessory hint, reset 'X' and redraw the layout.
|
| + removeView(firstSuggestion);
|
| + setTranslationX(0);
|
| + }
|
| + }
|
| + });
|
| + mReverseAnimationRunnable = new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + if (view.getVisibility() == View.VISIBLE) mAnimator.start();
|
| + }
|
| + };
|
| + view.postDelayed(mReverseAnimationRunnable, PAUSE_ANIMATION_BEFORE_REVERSE_MILLIS);
|
| + }
|
| }
|
|
|