1 // Copyright 2019 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chrome.browser.toolbar.top; 6 7 import android.content.Context; 8 import android.content.res.ColorStateList; 9 import android.graphics.Rect; 10 import android.graphics.drawable.Drawable; 11 import android.os.Build; 12 import android.util.AttributeSet; 13 import android.view.View; 14 import android.view.ViewPropertyAnimator; 15 import android.widget.ImageButton; 16 import android.widget.RelativeLayout; 17 18 import androidx.annotation.Nullable; 19 import androidx.annotation.StringRes; 20 import androidx.appcompat.content.res.AppCompatResources; 21 22 import org.chromium.chrome.R; 23 import org.chromium.chrome.browser.device.DeviceClassManager; 24 import org.chromium.chrome.browser.tabmodel.IncognitoStateProvider; 25 import org.chromium.chrome.browser.toolbar.NewTabButton; 26 import org.chromium.components.browser_ui.styles.ChromeColors; 27 import org.chromium.components.browser_ui.widget.animation.Interpolators; 28 import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter; 29 30 /** View of the StartSurfaceToolbar */ 31 class StartSurfaceToolbarView extends RelativeLayout { 32 private NewTabButton mNewTabButton; 33 private View mIncognitoSwitch; 34 private View mLogo; 35 @Nullable 36 private ImageButton mIdentityDiscButton; 37 private int mPrimaryColor; 38 private ColorStateList mLightIconTint; 39 private ColorStateList mDarkIconTint; 40 private ViewPropertyAnimator mVisibilityAnimator; 41 42 private Rect mLogoRect = new Rect(); 43 private Rect mViewRect = new Rect(); 44 45 private boolean mShouldShow; 46 private boolean mInStartSurfaceMode; 47 private boolean mIsShowing; 48 StartSurfaceToolbarView(Context context, AttributeSet attrs)49 public StartSurfaceToolbarView(Context context, AttributeSet attrs) { 50 super(context, attrs); 51 } 52 53 @Override onFinishInflate()54 protected void onFinishInflate() { 55 super.onFinishInflate(); 56 mNewTabButton = findViewById(R.id.new_tab_button); 57 mIncognitoSwitch = findViewById(R.id.incognito_switch); 58 mLogo = findViewById(R.id.logo); 59 mIdentityDiscButton = findViewById(R.id.identity_disc_button); 60 updatePrimaryColorAndTint(false); 61 } 62 63 @Override onLayout(boolean changed, int l, int t, int r, int b)64 protected void onLayout(boolean changed, int l, int t, int r, int b) { 65 // TODO(https://crbug.com/1040526) 66 67 super.onLayout(changed, l, t, r, b); 68 69 if (mLogo.getVisibility() == View.GONE) return; 70 71 mLogoRect.set(mLogo.getLeft(), mLogo.getTop(), mLogo.getRight(), mLogo.getBottom()); 72 for (int viewIndex = 0; viewIndex < getChildCount(); viewIndex++) { 73 View view = getChildAt(viewIndex); 74 if (view == mLogo || view.getVisibility() == View.GONE) continue; 75 76 assert view.getVisibility() == View.VISIBLE; 77 mViewRect.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); 78 if (Rect.intersects(mLogoRect, mViewRect)) { 79 mLogo.setVisibility(View.GONE); 80 break; 81 } 82 } 83 } 84 85 /** 86 * Sets the {@link OnClickListener} that will be notified when the New Tab button is pressed. 87 * @param listener The callback that will be notified when the New Tab button is pressed. 88 */ setOnNewTabClickHandler(View.OnClickListener listener)89 void setOnNewTabClickHandler(View.OnClickListener listener) { 90 mNewTabButton.setOnClickListener(listener); 91 } 92 93 /** 94 * Sets the Logo visibility. Logo will not show if screen is not wide enough. 95 * @param isVisible Whether the Logo should be visible. 96 */ setLogoVisibility(boolean isVisible)97 void setLogoVisibility(boolean isVisible) { 98 mLogo.setVisibility(isVisible ? View.VISIBLE : View.GONE); 99 } 100 101 /** 102 * @param isVisible Whether the menu button is visible. 103 */ setMenuButtonVisibility(boolean isVisible)104 void setMenuButtonVisibility(boolean isVisible) { 105 final int buttonPaddingLeft = getContext().getResources().getDimensionPixelOffset( 106 R.dimen.start_surface_toolbar_button_padding_to_button); 107 final int buttonPaddingRight = 108 (isVisible ? buttonPaddingLeft 109 : getContext().getResources().getDimensionPixelOffset( 110 R.dimen.start_surface_toolbar_button_padding_to_edge)); 111 mIdentityDiscButton.setPadding(buttonPaddingLeft, 0, buttonPaddingRight, 0); 112 mNewTabButton.setPadding(buttonPaddingLeft, 0, buttonPaddingRight, 0); 113 } 114 115 /** 116 * @param isVisible Whether the new tab button is visible. 117 */ setNewTabButtonVisibility(boolean isVisible)118 void setNewTabButtonVisibility(boolean isVisible) { 119 mNewTabButton.setVisibility(isVisible ? View.VISIBLE : View.GONE); 120 if (isVisible && Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { 121 // This is a workaround for the issue that the UrlBar is given the default focus on 122 // Android versions before Pie when showing the start surface toolbar with the new tab 123 // button (UrlBar is invisible to users). Check crbug.com/1081538 for more details. 124 mNewTabButton.getParent().requestChildFocus(mNewTabButton, mNewTabButton); 125 } 126 } 127 128 /** 129 * @param isClickable Whether the buttons are clickable. 130 */ setButtonClickableState(boolean isClickable)131 void setButtonClickableState(boolean isClickable) { 132 mNewTabButton.setClickable(isClickable); 133 mIncognitoSwitch.setClickable(isClickable); 134 } 135 136 /** 137 * @param isVisible Whether the Incognito switcher is visible. 138 */ setIncognitoSwitcherVisibility(boolean isVisible)139 void setIncognitoSwitcherVisibility(boolean isVisible) { 140 mIncognitoSwitch.setVisibility(isVisible ? VISIBLE : GONE); 141 } 142 143 /** 144 * @param isAtStart Whether the new tab button is at start. 145 */ setNewTabButtonAtStart(boolean isAtStart)146 void setNewTabButtonAtStart(boolean isAtStart) { 147 assert isAtStart; 148 if (isAtStart) { 149 ((LayoutParams) mNewTabButton.getLayoutParams()).removeRule(RelativeLayout.START_OF); 150 151 LayoutParams params = (LayoutParams) mIncognitoSwitch.getLayoutParams(); 152 params.removeRule(RelativeLayout.ALIGN_PARENT_START); 153 params.addRule(RelativeLayout.CENTER_HORIZONTAL); 154 } 155 } 156 157 /** 158 * @param highlight If the new tab button should be highlighted. 159 */ setNewTabButtonHighlight(boolean highlight)160 void setNewTabButtonHighlight(boolean highlight) { 161 if (mNewTabButton == null) return; 162 if (highlight) { 163 ViewHighlighter.turnOnCircularHighlight(mNewTabButton); 164 } else { 165 ViewHighlighter.turnOffHighlight(mNewTabButton); 166 } 167 } 168 169 /** Called when incognito mode changes. */ updateIncognito(boolean isIncognito)170 void updateIncognito(boolean isIncognito) { 171 updatePrimaryColorAndTint(isIncognito); 172 } 173 174 /** 175 * @param provider The {@link IncognitoStateProvider} passed to buttons that need access to it. 176 */ setIncognitoStateProvider(IncognitoStateProvider provider)177 void setIncognitoStateProvider(IncognitoStateProvider provider) { 178 mNewTabButton.setIncognitoStateProvider(provider); 179 } 180 181 /** Called when accessibility status changes. */ onAccessibilityStatusChanged(boolean enabled)182 void onAccessibilityStatusChanged(boolean enabled) { 183 mNewTabButton.onAccessibilityStatusChanged(); 184 } 185 186 /** @return The View for the identity disc. */ getIdentityDiscView()187 View getIdentityDiscView() { 188 return mIdentityDiscButton; 189 } 190 191 /** 192 * @param isAtStart Whether the identity disc is at start. 193 */ setIdentityDiscAtStart(boolean isAtStart)194 void setIdentityDiscAtStart(boolean isAtStart) { 195 ((LayoutParams) mIdentityDiscButton.getLayoutParams()).removeRule(RelativeLayout.START_OF); 196 } 197 198 /** 199 * @param isVisible Whether the identity disc is visible. 200 */ setIdentityDiscVisibility(boolean isVisible)201 void setIdentityDiscVisibility(boolean isVisible) { 202 mIdentityDiscButton.setVisibility(isVisible ? View.VISIBLE : View.GONE); 203 } 204 205 /** 206 * Sets the {@link OnClickListener} that will be notified when the identity disc button is 207 * pressed. 208 * @param listener The callback that will be notified when the identity disc is pressed. 209 */ setIdentityDiscClickHandler(View.OnClickListener listener)210 void setIdentityDiscClickHandler(View.OnClickListener listener) { 211 mIdentityDiscButton.setOnClickListener(listener); 212 } 213 214 /** 215 * Updates the image displayed on the identity disc button. 216 * @param image The new image for the button. 217 */ setIdentityDiscImage(Drawable image)218 void setIdentityDiscImage(Drawable image) { 219 mIdentityDiscButton.setImageDrawable(image); 220 } 221 222 /** 223 * Updates idnetity disc content description. 224 * @param contentDescriptionResId The new description for the button. 225 */ setIdentityDiscContentDescription(@tringRes int contentDescriptionResId)226 void setIdentityDiscContentDescription(@StringRes int contentDescriptionResId) { 227 mIdentityDiscButton.setContentDescription( 228 getContext().getResources().getString(contentDescriptionResId)); 229 } 230 231 /** 232 * Show or hide toolbar from tab. 233 * @param inStartSurfaceMode Whether or not toolbar should be shown or hidden. 234 * */ setStartSurfaceMode(boolean inStartSurfaceMode)235 void setStartSurfaceMode(boolean inStartSurfaceMode) { 236 mInStartSurfaceMode = inStartSurfaceMode; 237 // When showing or hiding toolbar from a tab, the fade-in and fade-out animations are not 238 // needed. (eg: cold start, changing theme, changing incognito status...) 239 showStartSurfaceToolbar(mInStartSurfaceMode && mShouldShow, false); 240 } 241 242 /** 243 * Show or hide toolbar. 244 * @param shouldShowStartSurfaceToolbar Whether or not toolbar should be shown or hidden. 245 * */ setToolbarVisibility(boolean shouldShowStartSurfaceToolbar)246 void setToolbarVisibility(boolean shouldShowStartSurfaceToolbar) { 247 mShouldShow = shouldShowStartSurfaceToolbar; 248 // When simply setting visibility, the animations should be shown. (eg: search box has 249 // focus) 250 showStartSurfaceToolbar(mInStartSurfaceMode && mShouldShow, true); 251 } 252 253 /** 254 * Start animation to show or hide toolbar. 255 * @param showStartSurfaceToolbar Whether or not toolbar should be shown or hidden. 256 * @param showAnimation Whether or not to show the animation. 257 */ showStartSurfaceToolbar(boolean showStartSurfaceToolbar, boolean showAnimation)258 private void showStartSurfaceToolbar(boolean showStartSurfaceToolbar, boolean showAnimation) { 259 if (showStartSurfaceToolbar == mIsShowing) return; 260 261 if (mVisibilityAnimator != null) { 262 mVisibilityAnimator.cancel(); 263 finishAnimation(showStartSurfaceToolbar); 264 } 265 266 mIsShowing = showStartSurfaceToolbar; 267 268 if (DeviceClassManager.enableAccessibilityLayout()) { 269 finishAnimation(showStartSurfaceToolbar); 270 return; 271 } 272 273 // TODO(https://crbug.com/1139024): Show the fade-in animation when 274 // TabUiFeatureUtilities#isTabToGtsAnimationEnabled is true. 275 if (!showAnimation) { 276 setVisibility(showStartSurfaceToolbar ? View.VISIBLE : View.GONE); 277 return; 278 } 279 280 // Show the fade-in and fade-out animation. Set visibility as VISIBLE here to show the 281 // animation. The visibility will be finally set in finishAnimation(). 282 setVisibility(View.VISIBLE); 283 setAlpha(showStartSurfaceToolbar ? 0.0f : 1.0f); 284 285 final long duration = TopToolbarCoordinator.TAB_SWITCHER_MODE_NORMAL_ANIMATION_DURATION_MS; 286 287 mVisibilityAnimator = 288 animate() 289 .alpha(showStartSurfaceToolbar ? 1.0f : 0.0f) 290 .setDuration(duration) 291 .setInterpolator(Interpolators.LINEAR_INTERPOLATOR) 292 .withEndAction(() -> { finishAnimation(showStartSurfaceToolbar); }); 293 } 294 finishAnimation(boolean showStartSurfaceToolbar)295 private void finishAnimation(boolean showStartSurfaceToolbar) { 296 setAlpha(1.0f); 297 setVisibility(showStartSurfaceToolbar ? View.VISIBLE : View.GONE); 298 mVisibilityAnimator = null; 299 } 300 updatePrimaryColorAndTint(boolean isIncognito)301 private void updatePrimaryColorAndTint(boolean isIncognito) { 302 int primaryColor = ChromeColors.getPrimaryBackgroundColor(getResources(), isIncognito); 303 setBackgroundColor(primaryColor); 304 305 if (mLightIconTint == null) { 306 mLightIconTint = AppCompatResources.getColorStateList( 307 getContext(), R.color.default_icon_color_light_tint_list); 308 mDarkIconTint = AppCompatResources.getColorStateList( 309 getContext(), R.color.default_icon_color_tint_list); 310 } 311 } 312 } 313