Commit e2325ea3 authored by Mugdha Lakhani's avatar Mugdha Lakhani Committed by Commit Bot

[WebLayer] Add animation to show and hide security icon.

When there's no security status icon to show, currently WebLayer
keeps the blank space for it. A more elegant UI is to start the URL bar
text at the leftmost edge in this case, without leaving any gap for the icon,
and animate to the right once the icon is ready to be shown.

This CL reuses code from Clank for the security button animation.

GIFs before and after the change can be seen here:
https://drive.google.com/drive/folders/1qyRRQzysAkFdKgwiKBk_aEqrvCB48M8i?usp=sharing

Bug: 1025607
Change-Id: I831ae02f1fdf096170a8296b3d8b8916c505aa70
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2132333
Commit-Queue: Mugdha Lakhani <nator@chromium.org>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Reviewed-by: default avatarPeter Conn <peconn@chromium.org>
Reviewed-by: default avatarColin Blundell <blundell@chromium.org>
Auto-Submit: Mugdha Lakhani <nator@chromium.org>
Cr-Commit-Position: refs/heads/master@{#756270}
parent dfee1b09
...@@ -200,7 +200,8 @@ public class CustomTabToolbar extends ToolbarLayout implements View.OnLongClickL ...@@ -200,7 +200,8 @@ public class CustomTabToolbar extends ToolbarLayout implements View.OnLongClickL
mCloseButton = findViewById(R.id.close_button); mCloseButton = findViewById(R.id.close_button);
mCloseButton.setOnLongClickListener(this); mCloseButton.setOnLongClickListener(this);
mMenuButton = findViewById(R.id.menu_button); mMenuButton = findViewById(R.id.menu_button);
mAnimDelegate = new CustomTabToolbarAnimationDelegate(mSecurityButton, mTitleUrlContainer); mAnimDelegate = new CustomTabToolbarAnimationDelegate(
mSecurityButton, mTitleUrlContainer, R.dimen.location_bar_icon_width);
} }
@Override @Override
...@@ -768,18 +769,12 @@ public class CustomTabToolbar extends ToolbarLayout implements View.OnLongClickL ...@@ -768,18 +769,12 @@ public class CustomTabToolbar extends ToolbarLayout implements View.OnLongClickL
int securityIconResource = getToolbarDataProvider().getSecurityIconResource( int securityIconResource = getToolbarDataProvider().getSecurityIconResource(
DeviceFormFactor.isNonMultiDisplayContextOnTablet(getContext())); DeviceFormFactor.isNonMultiDisplayContextOnTablet(getContext()));
if (securityIconResource == 0) { if (securityIconResource != 0) {
// Hide the button if we don't have an actual icon to display.
mSecurityButton.setImageDrawable(null);
mAnimDelegate.hideSecurityButton();
} else {
// ImageView#setImageResource is no-op if given resource is the current one.
mSecurityButton.setImageResource(securityIconResource);
ColorStateList colorStateList = AppCompatResources.getColorStateList( ColorStateList colorStateList = AppCompatResources.getColorStateList(
getContext(), getToolbarDataProvider().getSecurityIconColorStateList()); getContext(), getToolbarDataProvider().getSecurityIconColorStateList());
ApiCompatibilityUtils.setImageTintList(mSecurityButton, colorStateList); ApiCompatibilityUtils.setImageTintList(mSecurityButton, colorStateList);
mAnimDelegate.showSecurityButton();
} }
mAnimDelegate.updateSecurityButton(securityIconResource);
int contentDescriptionId = int contentDescriptionId =
getToolbarDataProvider().getSecurityIconContentDescriptionResourceId(); getToolbarDataProvider().getSecurityIconContentDescriptionResourceId();
......
...@@ -6,15 +6,16 @@ package org.chromium.chrome.browser.toolbar.top; ...@@ -6,15 +6,16 @@ package org.chromium.chrome.browser.toolbar.top;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context; import android.content.Context;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.View; import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.DimenRes;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.components.browser_ui.widget.animation.CancelAwareAnimatorListener; import org.chromium.components.omnibox.SecurityButtonAnimationDelegate;
import org.chromium.ui.interpolators.BakedBezierInterpolator; import org.chromium.ui.interpolators.BakedBezierInterpolator;
/** /**
...@@ -30,63 +31,23 @@ import org.chromium.ui.interpolators.BakedBezierInterpolator; ...@@ -30,63 +31,23 @@ import org.chromium.ui.interpolators.BakedBezierInterpolator;
* its new position. * its new position.
*/ */
class CustomTabToolbarAnimationDelegate { class CustomTabToolbarAnimationDelegate {
private static final int CUSTOM_TAB_TOOLBAR_SLIDE_DURATION_MS = 200; private final SecurityButtonAnimationDelegate mSecurityButtonAnimationDelegate;
private static final int CUSTOM_TAB_TOOLBAR_FADE_DURATION_MS = 150;
private final View mSecurityButton;
private final View mTitleUrlContainer;
private final AnimatorSet mSecurityButtonShowAnimator;
private final AnimatorSet mSecurityButtonHideAnimator;
private TextView mUrlBar; private TextView mUrlBar;
private TextView mTitleBar; private TextView mTitleBar;
private int mSecurityButtonWidth;
// A flag controlling whether the animation has run before. // A flag controlling whether the animation has run before.
private boolean mShouldRunTitleAnimation; private boolean mShouldRunTitleAnimation;
/** /**
* Constructs an instance of {@link CustomTabToolbarAnimationDelegate}. * Constructs an instance of {@link CustomTabToolbarAnimationDelegate}.
*/ */
CustomTabToolbarAnimationDelegate(View securityButton, final View titleUrlContainer) { CustomTabToolbarAnimationDelegate(ImageButton securityButton, final View titleUrlContainer,
mSecurityButton = securityButton; @DimenRes int securityStatusIconSize) {
mTitleUrlContainer = titleUrlContainer; int securityButtonWidth =
mSecurityButtonWidth = securityButton.getResources().getDimensionPixelSize( securityButton.getResources().getDimensionPixelSize(securityStatusIconSize);
R.dimen.location_bar_icon_width); titleUrlContainer.setTranslationX(-securityButtonWidth);
mSecurityButtonAnimationDelegate = new SecurityButtonAnimationDelegate(
titleUrlContainer.setTranslationX(-mSecurityButtonWidth); securityButton, titleUrlContainer, securityStatusIconSize);
mSecurityButtonShowAnimator = new AnimatorSet();
Animator translateRight = ObjectAnimator.ofFloat(titleUrlContainer, View.TRANSLATION_X, 0);
translateRight.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE);
translateRight.setDuration(CUSTOM_TAB_TOOLBAR_SLIDE_DURATION_MS);
Animator fadeIn = ObjectAnimator.ofFloat(mSecurityButton, View.ALPHA, 1);
fadeIn.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE);
fadeIn.setDuration(CUSTOM_TAB_TOOLBAR_FADE_DURATION_MS);
fadeIn.addListener(new CancelAwareAnimatorListener() {
@Override
public void onStart(Animator animation) {
mSecurityButton.setVisibility(View.VISIBLE);
}
});
mSecurityButtonShowAnimator.playSequentially(translateRight, fadeIn);
mSecurityButtonHideAnimator = new AnimatorSet();
Animator fadeOut = ObjectAnimator.ofFloat(mSecurityButton, View.ALPHA, 0);
fadeOut.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE);
fadeOut.setDuration(CUSTOM_TAB_TOOLBAR_FADE_DURATION_MS);
fadeOut.addListener(new CancelAwareAnimatorListener() {
@Override
public void onEnd(Animator animation) {
mSecurityButton.setVisibility(View.INVISIBLE);
}
});
Animator translateLeft = ObjectAnimator.ofFloat(
titleUrlContainer, View.TRANSLATION_X, -mSecurityButtonWidth);
translateLeft.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE);
translateLeft.setDuration(CUSTOM_TAB_TOOLBAR_SLIDE_DURATION_MS);
mSecurityButtonHideAnimator.playSequentially(fadeOut, translateLeft);
} }
/** /**
...@@ -147,7 +108,7 @@ class CustomTabToolbarAnimationDelegate { ...@@ -147,7 +108,7 @@ class CustomTabToolbarAnimationDelegate {
.scaleY(1f) .scaleY(1f)
.translationX(0) .translationX(0)
.translationY(0) .translationY(0)
.setDuration(CUSTOM_TAB_TOOLBAR_SLIDE_DURATION_MS) .setDuration(SecurityButtonAnimationDelegate.SLIDE_DURATION_MS)
.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE) .setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE)
.setListener(new AnimatorListenerAdapter() { .setListener(new AnimatorListenerAdapter() {
@Override @Override
...@@ -155,7 +116,8 @@ class CustomTabToolbarAnimationDelegate { ...@@ -155,7 +116,8 @@ class CustomTabToolbarAnimationDelegate {
mTitleBar.animate() mTitleBar.animate()
.alpha(1f) .alpha(1f)
.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE) .setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE)
.setDuration(CUSTOM_TAB_TOOLBAR_FADE_DURATION_MS) .setDuration(
SecurityButtonAnimationDelegate.FADE_DURATION_MS)
.start(); .start();
} }
}) })
...@@ -165,26 +127,11 @@ class CustomTabToolbarAnimationDelegate { ...@@ -165,26 +127,11 @@ class CustomTabToolbarAnimationDelegate {
} }
/** /**
* Starts the animation to show the security button. * Starts the animation to show/hide the security button,
*/ * @param securityIconResource The updated resource to be assigned to the security status icon.
void showSecurityButton() { * When this is null, the icon is animated to the left and faded out.
if (mSecurityButtonHideAnimator.isStarted()) mSecurityButtonHideAnimator.cancel();
if (mSecurityButtonShowAnimator.isStarted()
|| mSecurityButton.getVisibility() == View.VISIBLE) {
return;
}
mSecurityButtonShowAnimator.start();
}
/**
* Starts the animation to hide the security button.
*/ */
void hideSecurityButton() { void updateSecurityButton(int securityIconResource) {
if (mSecurityButtonShowAnimator.isStarted()) mSecurityButtonShowAnimator.cancel(); mSecurityButtonAnimationDelegate.updateSecurityButton(securityIconResource);
if (mSecurityButtonHideAnimator.isStarted()
|| mTitleUrlContainer.getTranslationX() == -mSecurityButtonWidth) {
return;
}
mSecurityButtonHideAnimator.start();
} }
} }
...@@ -304,6 +304,7 @@ if (is_android) { ...@@ -304,6 +304,7 @@ if (is_android) {
sources = [ sources = [
"android/java/src/org/chromium/components/omnibox/AutocompleteSchemeClassifier.java", "android/java/src/org/chromium/components/omnibox/AutocompleteSchemeClassifier.java",
"android/java/src/org/chromium/components/omnibox/OmniboxUrlEmphasizer.java", "android/java/src/org/chromium/components/omnibox/OmniboxUrlEmphasizer.java",
"android/java/src/org/chromium/components/omnibox/SecurityButtonAnimationDelegate.java",
"android/java/src/org/chromium/components/omnibox/SecurityStatusIcon.java", "android/java/src/org/chromium/components/omnibox/SecurityStatusIcon.java",
"android/java/src/org/chromium/components/omnibox/SuggestionAnswer.java", "android/java/src/org/chromium/components/omnibox/SuggestionAnswer.java",
] ]
...@@ -312,10 +313,12 @@ if (is_android) { ...@@ -312,10 +313,12 @@ if (is_android) {
":java_resources", ":java_resources",
"//base:base_java", "//base:base_java",
"//base:jni_java", "//base:jni_java",
"//components/browser_ui/widget/android:java",
"//components/embedder_support/android:util_java", "//components/embedder_support/android:util_java",
"//components/security_state/core:security_state_enums_java", "//components/security_state/core:security_state_enums_java",
"//content/public/android:content_java", "//content/public/android:content_java",
"//third_party/android_deps:androidx_core_core_java", "//third_party/android_deps:androidx_core_core_java",
"//ui/android:ui_full_java",
] ]
srcjar_deps = [ ":browser_java_enums_srcjar" ] srcjar_deps = [ ":browser_java_enums_srcjar" ]
......
include_rules = [ include_rules = [
"+components/bookmarks/browser", "+components/bookmarks/browser",
"+components/bookmarks/test", "+components/bookmarks/test",
"+components/browser_ui/widget/android/java",
"+components/component_updater", "+components/component_updater",
"+components/dom_distiller/core", "+components/dom_distiller/core",
"+components/embedder_support/android/java/src/org/chromium/components/embedder_support/util", "+components/embedder_support/android/java/src/org/chromium/components/embedder_support/util",
...@@ -42,6 +43,7 @@ include_rules = [ ...@@ -42,6 +43,7 @@ include_rules = [
"+third_party/protobuf/src/google", "+third_party/protobuf/src/google",
"+third_party/re2", "+third_party/re2",
"+third_party/skia", "+third_party/skia",
"+ui/android/java",
"+ui/base", "+ui/base",
"+ui/gfx", "+ui/gfx",
"+url", "+url",
......
// Copyright 2020 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.omnibox;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.view.View;
import android.widget.ImageButton;
import androidx.annotation.DimenRes;
import org.chromium.components.browser_ui.widget.animation.CancelAwareAnimatorListener;
import org.chromium.ui.interpolators.BakedBezierInterpolator;
/**
* Helper class to animate the security status icon.
*/
public class SecurityButtonAnimationDelegate {
public static final int SLIDE_DURATION_MS = 200;
public static final int FADE_DURATION_MS = 150;
private final ImageButton mSecurityButton;
private final View mTitleUrlContainer;
private final AnimatorSet mSecurityButtonShowAnimator;
private final AnimatorSet mSecurityButtonHideAnimator;
private final int mSecurityButtonWidth;
public SecurityButtonAnimationDelegate(
ImageButton securityButton, View urlTextView, @DimenRes int securityButtonIconSize) {
mSecurityButton = securityButton;
mTitleUrlContainer = urlTextView;
mSecurityButtonWidth =
mSecurityButton.getResources().getDimensionPixelSize(securityButtonIconSize);
mSecurityButtonShowAnimator = new AnimatorSet();
Animator translateRight = ObjectAnimator.ofFloat(mTitleUrlContainer, View.TRANSLATION_X, 0);
translateRight.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE);
translateRight.setDuration(SLIDE_DURATION_MS);
Animator fadeIn = ObjectAnimator.ofFloat(mSecurityButton, View.ALPHA, 1);
fadeIn.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE);
fadeIn.setDuration(FADE_DURATION_MS);
fadeIn.addListener(new CancelAwareAnimatorListener() {
@Override
public void onStart(Animator animation) {
mSecurityButton.setVisibility(View.VISIBLE);
}
});
mSecurityButtonShowAnimator.playSequentially(translateRight, fadeIn);
mSecurityButtonHideAnimator = new AnimatorSet();
Animator fadeOut = ObjectAnimator.ofFloat(mSecurityButton, View.ALPHA, 0);
fadeOut.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE);
fadeOut.setDuration(FADE_DURATION_MS);
fadeOut.addListener(new CancelAwareAnimatorListener() {
@Override
public void onEnd(Animator animation) {
mSecurityButton.setVisibility(View.INVISIBLE);
}
});
Animator translateLeft = ObjectAnimator.ofFloat(
mTitleUrlContainer, View.TRANSLATION_X, -mSecurityButtonWidth);
translateLeft.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE);
translateLeft.setDuration(SLIDE_DURATION_MS);
mSecurityButtonHideAnimator.playSequentially(fadeOut, translateLeft);
}
/**
* Based on |securityIconResource|, animates the security status icon in or out.
* @param securityIconResource The updated resource to be assigned to the security status icon.
* When this is null, the icon is animated to the left and faded out.
*/
public void updateSecurityButton(int securityIconResource) {
if (securityIconResource == 0) {
// No icon to display.
mSecurityButton.setImageDrawable(null);
hideSecurityButton();
} else {
// ImageView#setImageResource is no-op if given resource is the current one.
mSecurityButton.setImageResource(securityIconResource);
showSecurityButton();
}
}
/**
* Starts the animation to show the security button.
*/
private void showSecurityButton() {
if (mSecurityButtonHideAnimator.isStarted()) mSecurityButtonHideAnimator.cancel();
if (mSecurityButtonShowAnimator.isStarted()
|| mSecurityButton.getVisibility() == View.VISIBLE) {
return;
}
mSecurityButtonShowAnimator.start();
}
/**
* Starts the animation to hide the security button.
*/
private void hideSecurityButton() {
if (mSecurityButtonShowAnimator.isStarted()) mSecurityButtonShowAnimator.cancel();
if (mSecurityButtonHideAnimator.isStarted()
|| mTitleUrlContainer.getTranslationX() == -mSecurityButtonWidth) {
return;
}
mSecurityButtonHideAnimator.start();
}
}
...@@ -19,6 +19,7 @@ import androidx.annotation.NonNull; ...@@ -19,6 +19,7 @@ import androidx.annotation.NonNull;
import org.chromium.base.LifetimeAssert; import org.chromium.base.LifetimeAssert;
import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods; import org.chromium.base.annotations.NativeMethods;
import org.chromium.components.omnibox.SecurityButtonAnimationDelegate;
import org.chromium.components.omnibox.SecurityStatusIcon; import org.chromium.components.omnibox.SecurityStatusIcon;
import org.chromium.weblayer_private.interfaces.IObjectWrapper; import org.chromium.weblayer_private.interfaces.IObjectWrapper;
import org.chromium.weblayer_private.interfaces.IUrlBarController; import org.chromium.weblayer_private.interfaces.IUrlBarController;
...@@ -77,6 +78,7 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub { ...@@ -77,6 +78,7 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub {
private float mTextSize; private float mTextSize;
private TextView mUrlTextView; private TextView mUrlTextView;
private ImageButton mSecurityButton; private ImageButton mSecurityButton;
private final SecurityButtonAnimationDelegate mSecurityButtonAnimationDelegate;
public UrlBarView(@NonNull Context context, Bundle options) { public UrlBarView(@NonNull Context context, Bundle options) {
super(context); super(context);
...@@ -86,6 +88,8 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub { ...@@ -86,6 +88,8 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub {
setBackgroundColor(Color.TRANSPARENT); setBackgroundColor(Color.TRANSPARENT);
mUrlTextView = findViewById(R.id.url_text); mUrlTextView = findViewById(R.id.url_text);
mSecurityButton = (ImageButton) findViewById(R.id.security_button); mSecurityButton = (ImageButton) findViewById(R.id.security_button);
mSecurityButtonAnimationDelegate = new SecurityButtonAnimationDelegate(
mSecurityButton, mUrlTextView, R.dimen.security_status_icon_size);
updateView(); updateView();
} }
...@@ -120,7 +124,7 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub { ...@@ -120,7 +124,7 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub {
mUrlTextView.setTextSize( mUrlTextView.setTextSize(
TypedValue.COMPLEX_UNIT_SP, Math.max(MINIMUM_TEXT_SIZE, mTextSize)); TypedValue.COMPLEX_UNIT_SP, Math.max(MINIMUM_TEXT_SIZE, mTextSize));
mSecurityButton.setImageResource(getSecurityIcon()); mSecurityButtonAnimationDelegate.updateSecurityButton(getSecurityIcon());
mSecurityButton.setContentDescription(getContext().getResources().getString( mSecurityButton.setContentDescription(getContext().getResources().getString(
SecurityStatusIcon.getSecurityIconContentDescriptionResourceId( SecurityStatusIcon.getSecurityIconContentDescriptionResourceId(
UrlBarControllerImplJni.get().getConnectionSecurityLevel( UrlBarControllerImplJni.get().getConnectionSecurityLevel(
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
android:layout_height="@dimen/security_status_icon_size" android:layout_height="@dimen/security_status_icon_size"
android:layout_width="@dimen/security_status_icon_size" android:layout_width="@dimen/security_status_icon_size"
android:scaleType="center" android:scaleType="center"
android:visibility="gone"
app:tint="@color/default_icon_color_tint_list"/> app:tint="@color/default_icon_color_tint_list"/>
<TextView <TextView
......
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