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
mCloseButton = findViewById(R.id.close_button);
mCloseButton.setOnLongClickListener(this);
mMenuButton = findViewById(R.id.menu_button);
mAnimDelegate = new CustomTabToolbarAnimationDelegate(mSecurityButton, mTitleUrlContainer);
mAnimDelegate = new CustomTabToolbarAnimationDelegate(
mSecurityButton, mTitleUrlContainer, R.dimen.location_bar_icon_width);
}
@Override
......@@ -768,18 +769,12 @@ public class CustomTabToolbar extends ToolbarLayout implements View.OnLongClickL
int securityIconResource = getToolbarDataProvider().getSecurityIconResource(
DeviceFormFactor.isNonMultiDisplayContextOnTablet(getContext()));
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);
if (securityIconResource != 0) {
ColorStateList colorStateList = AppCompatResources.getColorStateList(
getContext(), getToolbarDataProvider().getSecurityIconColorStateList());
ApiCompatibilityUtils.setImageTintList(mSecurityButton, colorStateList);
mAnimDelegate.showSecurityButton();
}
mAnimDelegate.updateSecurityButton(securityIconResource);
int contentDescriptionId =
getToolbarDataProvider().getSecurityIconContentDescriptionResourceId();
......
......@@ -6,15 +6,16 @@ package org.chromium.chrome.browser.toolbar.top;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.util.TypedValue;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;
import androidx.annotation.DimenRes;
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;
/**
......@@ -30,63 +31,23 @@ import org.chromium.ui.interpolators.BakedBezierInterpolator;
* its new position.
*/
class CustomTabToolbarAnimationDelegate {
private static final int CUSTOM_TAB_TOOLBAR_SLIDE_DURATION_MS = 200;
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 final SecurityButtonAnimationDelegate mSecurityButtonAnimationDelegate;
private TextView mUrlBar;
private TextView mTitleBar;
private int mSecurityButtonWidth;
// A flag controlling whether the animation has run before.
private boolean mShouldRunTitleAnimation;
/**
* Constructs an instance of {@link CustomTabToolbarAnimationDelegate}.
*/
CustomTabToolbarAnimationDelegate(View securityButton, final View titleUrlContainer) {
mSecurityButton = securityButton;
mTitleUrlContainer = titleUrlContainer;
mSecurityButtonWidth = securityButton.getResources().getDimensionPixelSize(
R.dimen.location_bar_icon_width);
titleUrlContainer.setTranslationX(-mSecurityButtonWidth);
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);
CustomTabToolbarAnimationDelegate(ImageButton securityButton, final View titleUrlContainer,
@DimenRes int securityStatusIconSize) {
int securityButtonWidth =
securityButton.getResources().getDimensionPixelSize(securityStatusIconSize);
titleUrlContainer.setTranslationX(-securityButtonWidth);
mSecurityButtonAnimationDelegate = new SecurityButtonAnimationDelegate(
securityButton, titleUrlContainer, securityStatusIconSize);
}
/**
......@@ -147,7 +108,7 @@ class CustomTabToolbarAnimationDelegate {
.scaleY(1f)
.translationX(0)
.translationY(0)
.setDuration(CUSTOM_TAB_TOOLBAR_SLIDE_DURATION_MS)
.setDuration(SecurityButtonAnimationDelegate.SLIDE_DURATION_MS)
.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE)
.setListener(new AnimatorListenerAdapter() {
@Override
......@@ -155,7 +116,8 @@ class CustomTabToolbarAnimationDelegate {
mTitleBar.animate()
.alpha(1f)
.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE)
.setDuration(CUSTOM_TAB_TOOLBAR_FADE_DURATION_MS)
.setDuration(
SecurityButtonAnimationDelegate.FADE_DURATION_MS)
.start();
}
})
......@@ -165,26 +127,11 @@ class CustomTabToolbarAnimationDelegate {
}
/**
* Starts the animation to show the security button.
*/
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.
* Starts the animation to show/hide the security button,
* @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.
*/
void hideSecurityButton() {
if (mSecurityButtonShowAnimator.isStarted()) mSecurityButtonShowAnimator.cancel();
if (mSecurityButtonHideAnimator.isStarted()
|| mTitleUrlContainer.getTranslationX() == -mSecurityButtonWidth) {
return;
}
mSecurityButtonHideAnimator.start();
void updateSecurityButton(int securityIconResource) {
mSecurityButtonAnimationDelegate.updateSecurityButton(securityIconResource);
}
}
......@@ -304,6 +304,7 @@ if (is_android) {
sources = [
"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/SecurityButtonAnimationDelegate.java",
"android/java/src/org/chromium/components/omnibox/SecurityStatusIcon.java",
"android/java/src/org/chromium/components/omnibox/SuggestionAnswer.java",
]
......@@ -312,10 +313,12 @@ if (is_android) {
":java_resources",
"//base:base_java",
"//base:jni_java",
"//components/browser_ui/widget/android:java",
"//components/embedder_support/android:util_java",
"//components/security_state/core:security_state_enums_java",
"//content/public/android:content_java",
"//third_party/android_deps:androidx_core_core_java",
"//ui/android:ui_full_java",
]
srcjar_deps = [ ":browser_java_enums_srcjar" ]
......
include_rules = [
"+components/bookmarks/browser",
"+components/bookmarks/test",
"+components/browser_ui/widget/android/java",
"+components/component_updater",
"+components/dom_distiller/core",
"+components/embedder_support/android/java/src/org/chromium/components/embedder_support/util",
......@@ -42,6 +43,7 @@ include_rules = [
"+third_party/protobuf/src/google",
"+third_party/re2",
"+third_party/skia",
"+ui/android/java",
"+ui/base",
"+ui/gfx",
"+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;
import org.chromium.base.LifetimeAssert;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.components.omnibox.SecurityButtonAnimationDelegate;
import org.chromium.components.omnibox.SecurityStatusIcon;
import org.chromium.weblayer_private.interfaces.IObjectWrapper;
import org.chromium.weblayer_private.interfaces.IUrlBarController;
......@@ -77,6 +78,7 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub {
private float mTextSize;
private TextView mUrlTextView;
private ImageButton mSecurityButton;
private final SecurityButtonAnimationDelegate mSecurityButtonAnimationDelegate;
public UrlBarView(@NonNull Context context, Bundle options) {
super(context);
......@@ -86,6 +88,8 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub {
setBackgroundColor(Color.TRANSPARENT);
mUrlTextView = findViewById(R.id.url_text);
mSecurityButton = (ImageButton) findViewById(R.id.security_button);
mSecurityButtonAnimationDelegate = new SecurityButtonAnimationDelegate(
mSecurityButton, mUrlTextView, R.dimen.security_status_icon_size);
updateView();
}
......@@ -120,7 +124,7 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub {
mUrlTextView.setTextSize(
TypedValue.COMPLEX_UNIT_SP, Math.max(MINIMUM_TEXT_SIZE, mTextSize));
mSecurityButton.setImageResource(getSecurityIcon());
mSecurityButtonAnimationDelegate.updateSecurityButton(getSecurityIcon());
mSecurityButton.setContentDescription(getContext().getResources().getString(
SecurityStatusIcon.getSecurityIconContentDescriptionResourceId(
UrlBarControllerImplJni.get().getConnectionSecurityLevel(
......
......@@ -17,6 +17,7 @@
android:layout_height="@dimen/security_status_icon_size"
android:layout_width="@dimen/security_status_icon_size"
android:scaleType="center"
android:visibility="gone"
app:tint="@color/default_icon_color_tint_list"/>
<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