1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.systemui.statusbar.phone; 18 19 import static com.android.systemui.DejankUtils.whitelistIpcs; 20 import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection; 21 import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN; 22 import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT; 23 24 import android.animation.ValueAnimator; 25 import android.annotation.ColorInt; 26 import android.content.Context; 27 import android.content.res.Configuration; 28 import android.content.res.Resources; 29 import android.graphics.Color; 30 import android.graphics.Rect; 31 import android.graphics.drawable.Drawable; 32 import android.os.UserManager; 33 import android.util.AttributeSet; 34 import android.util.Pair; 35 import android.util.TypedValue; 36 import android.view.DisplayCutout; 37 import android.view.Gravity; 38 import android.view.View; 39 import android.view.ViewGroup; 40 import android.view.ViewTreeObserver; 41 import android.view.WindowInsets; 42 import android.widget.ImageView; 43 import android.widget.LinearLayout; 44 import android.widget.RelativeLayout; 45 import android.widget.TextView; 46 47 import com.android.settingslib.Utils; 48 import com.android.systemui.BatteryMeterView; 49 import com.android.systemui.Dependency; 50 import com.android.systemui.R; 51 import com.android.systemui.animation.Interpolators; 52 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; 53 import com.android.systemui.statusbar.FeatureFlags; 54 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; 55 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; 56 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager; 57 import com.android.systemui.statusbar.policy.BatteryController; 58 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; 59 import com.android.systemui.statusbar.policy.ConfigurationController; 60 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; 61 import com.android.systemui.statusbar.policy.UserInfoController; 62 import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener; 63 import com.android.systemui.statusbar.policy.UserInfoControllerImpl; 64 65 import java.io.FileDescriptor; 66 import java.io.PrintWriter; 67 import java.util.ArrayList; 68 import java.util.List; 69 70 /** 71 * The header group on Keyguard. 72 */ 73 public class KeyguardStatusBarView extends RelativeLayout implements 74 BatteryStateChangeCallback, 75 OnUserInfoChangedListener, 76 ConfigurationListener, 77 SystemStatusAnimationCallback { 78 79 private static final int LAYOUT_NONE = 0; 80 private static final int LAYOUT_CUTOUT = 1; 81 private static final int LAYOUT_NO_CUTOUT = 2; 82 83 private final Rect mEmptyRect = new Rect(0, 0, 0, 0); 84 85 private boolean mShowPercentAvailable; 86 private boolean mBatteryCharging; 87 private boolean mBatteryListening; 88 89 private TextView mCarrierLabel; 90 private ImageView mMultiUserAvatar; 91 private BatteryMeterView mBatteryView; 92 private StatusIconContainer mStatusIconContainer; 93 94 private BatteryController mBatteryController; 95 private boolean mKeyguardUserSwitcherEnabled; 96 private final UserManager mUserManager; 97 98 private int mSystemIconsSwitcherHiddenExpandedMargin; 99 private int mSystemIconsBaseMargin; 100 private View mSystemIconsContainer; 101 private TintedIconManager mIconManager; 102 private List<String> mBlockedIcons = new ArrayList<>(); 103 104 private View mCutoutSpace; 105 private ViewGroup mStatusIconArea; 106 private int mLayoutState = LAYOUT_NONE; 107 108 private SystemStatusAnimationScheduler mAnimationScheduler; 109 private FeatureFlags mFeatureFlags; 110 111 /** 112 * Draw this many pixels into the left/right side of the cutout to optimally use the space 113 */ 114 private int mCutoutSideNudge = 0; 115 116 private DisplayCutout mDisplayCutout; 117 private int mRoundedCornerPadding = 0; 118 // right and left padding applied to this view to account for cutouts and rounded corners 119 private Pair<Integer, Integer> mPadding = new Pair(0, 0); 120 121 /** 122 * The clipping on the top 123 */ 124 private int mTopClipping; 125 private final Rect mClipRect = new Rect(0, 0, 0, 0); 126 KeyguardStatusBarView(Context context, AttributeSet attrs)127 public KeyguardStatusBarView(Context context, AttributeSet attrs) { 128 super(context, attrs); 129 mUserManager = UserManager.get(getContext()); 130 } 131 132 @Override onFinishInflate()133 protected void onFinishInflate() { 134 super.onFinishInflate(); 135 mSystemIconsContainer = findViewById(R.id.system_icons_container); 136 mMultiUserAvatar = findViewById(R.id.multi_user_avatar); 137 mCarrierLabel = findViewById(R.id.keyguard_carrier_text); 138 mBatteryView = mSystemIconsContainer.findViewById(R.id.battery); 139 mCutoutSpace = findViewById(R.id.cutout_space_view); 140 mStatusIconArea = findViewById(R.id.status_icon_area); 141 mStatusIconContainer = findViewById(R.id.statusIcons); 142 143 loadDimens(); 144 loadBlockList(); 145 mBatteryController = Dependency.get(BatteryController.class); 146 mAnimationScheduler = Dependency.get(SystemStatusAnimationScheduler.class); 147 mFeatureFlags = Dependency.get(FeatureFlags.class); 148 } 149 150 @Override onConfigurationChanged(Configuration newConfig)151 protected void onConfigurationChanged(Configuration newConfig) { 152 super.onConfigurationChanged(newConfig); 153 154 MarginLayoutParams lp = (MarginLayoutParams) mMultiUserAvatar.getLayoutParams(); 155 lp.width = lp.height = getResources().getDimensionPixelSize( 156 R.dimen.multi_user_avatar_keyguard_size); 157 mMultiUserAvatar.setLayoutParams(lp); 158 159 // System icons 160 lp = (MarginLayoutParams) mSystemIconsContainer.getLayoutParams(); 161 lp.setMarginStart(getResources().getDimensionPixelSize( 162 R.dimen.system_icons_super_container_margin_start)); 163 mSystemIconsContainer.setLayoutParams(lp); 164 mSystemIconsContainer.setPaddingRelative(mSystemIconsContainer.getPaddingStart(), 165 mSystemIconsContainer.getPaddingTop(), 166 getResources().getDimensionPixelSize(R.dimen.system_icons_keyguard_padding_end), 167 mSystemIconsContainer.getPaddingBottom()); 168 169 // Respect font size setting. 170 mCarrierLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, 171 getResources().getDimensionPixelSize( 172 com.android.internal.R.dimen.text_size_small_material)); 173 lp = (MarginLayoutParams) mCarrierLabel.getLayoutParams(); 174 175 int marginStart = calculateMargin( 176 getResources().getDimensionPixelSize(R.dimen.keyguard_carrier_text_margin), 177 mPadding.first); 178 lp.setMarginStart(marginStart); 179 180 mCarrierLabel.setLayoutParams(lp); 181 updateKeyguardStatusBarHeight(); 182 } 183 updateKeyguardStatusBarHeight()184 private void updateKeyguardStatusBarHeight() { 185 final int waterfallTop = 186 mDisplayCutout == null ? 0 : mDisplayCutout.getWaterfallInsets().top; 187 MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); 188 lp.height = getResources().getDimensionPixelSize( 189 R.dimen.status_bar_header_height_keyguard) + waterfallTop; 190 setLayoutParams(lp); 191 } 192 loadDimens()193 private void loadDimens() { 194 Resources res = getResources(); 195 mSystemIconsSwitcherHiddenExpandedMargin = res.getDimensionPixelSize( 196 R.dimen.system_icons_switcher_hidden_expanded_margin); 197 mSystemIconsBaseMargin = res.getDimensionPixelSize( 198 R.dimen.system_icons_super_container_avatarless_margin_end); 199 mCutoutSideNudge = getResources().getDimensionPixelSize( 200 R.dimen.display_cutout_margin_consumption); 201 mShowPercentAvailable = getContext().getResources().getBoolean( 202 com.android.internal.R.bool.config_battery_percentage_setting_available); 203 mRoundedCornerPadding = res.getDimensionPixelSize( 204 R.dimen.rounded_corner_content_padding); 205 } 206 207 // Set hidden status bar items loadBlockList()208 private void loadBlockList() { 209 Resources r = getResources(); 210 mBlockedIcons.add(r.getString(com.android.internal.R.string.status_bar_volume)); 211 mBlockedIcons.add(r.getString(com.android.internal.R.string.status_bar_alarm_clock)); 212 mBlockedIcons.add(r.getString(com.android.internal.R.string.status_bar_call_strength)); 213 } 214 updateVisibilities()215 private void updateVisibilities() { 216 if (mMultiUserAvatar.getParent() != mStatusIconArea 217 && !mKeyguardUserSwitcherEnabled) { 218 if (mMultiUserAvatar.getParent() != null) { 219 getOverlay().remove(mMultiUserAvatar); 220 } 221 mStatusIconArea.addView(mMultiUserAvatar, 0); 222 } else if (mMultiUserAvatar.getParent() == mStatusIconArea 223 && mKeyguardUserSwitcherEnabled) { 224 mStatusIconArea.removeView(mMultiUserAvatar); 225 } 226 if (!mKeyguardUserSwitcherEnabled) { 227 // If we have no keyguard switcher, the screen width is under 600dp. In this case, 228 // we only show the multi-user switch if it's enabled through UserManager as well as 229 // by the user. 230 // TODO(b/138661450) Move IPC calls to background 231 boolean isMultiUserEnabled = whitelistIpcs(() -> mUserManager.isUserSwitcherEnabled( 232 mContext.getResources().getBoolean( 233 R.bool.qs_show_user_switcher_for_single_user))); 234 if (isMultiUserEnabled) { 235 mMultiUserAvatar.setVisibility(View.VISIBLE); 236 } else { 237 mMultiUserAvatar.setVisibility(View.GONE); 238 } 239 } 240 mBatteryView.setForceShowPercent(mBatteryCharging && mShowPercentAvailable); 241 } 242 updateSystemIconsLayoutParams()243 private void updateSystemIconsLayoutParams() { 244 LinearLayout.LayoutParams lp = 245 (LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams(); 246 // If the avatar icon is gone, we need to have some end margin to display the system icons 247 // correctly. 248 int baseMarginEnd = mMultiUserAvatar.getVisibility() == View.GONE 249 ? mSystemIconsBaseMargin 250 : 0; 251 int marginEnd = 252 mKeyguardUserSwitcherEnabled ? mSystemIconsSwitcherHiddenExpandedMargin 253 : baseMarginEnd; 254 marginEnd = calculateMargin(marginEnd, mPadding.second); 255 if (marginEnd != lp.getMarginEnd()) { 256 lp.setMarginEnd(marginEnd); 257 mSystemIconsContainer.setLayoutParams(lp); 258 } 259 } 260 261 @Override onApplyWindowInsets(WindowInsets insets)262 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 263 mLayoutState = LAYOUT_NONE; 264 if (updateLayoutConsideringCutout()) { 265 requestLayout(); 266 } 267 return super.onApplyWindowInsets(insets); 268 } 269 updateLayoutConsideringCutout()270 private boolean updateLayoutConsideringCutout() { 271 mDisplayCutout = getRootWindowInsets().getDisplayCutout(); 272 updateKeyguardStatusBarHeight(); 273 274 Pair<Integer, Integer> cornerCutoutMargins = 275 StatusBarWindowView.cornerCutoutMargins(mDisplayCutout, getDisplay()); 276 updatePadding(cornerCutoutMargins); 277 if (mDisplayCutout == null || cornerCutoutMargins != null) { 278 return updateLayoutParamsNoCutout(); 279 } else { 280 return updateLayoutParamsForCutout(); 281 } 282 } 283 updatePadding(Pair<Integer, Integer> cornerCutoutMargins)284 private void updatePadding(Pair<Integer, Integer> cornerCutoutMargins) { 285 final int waterfallTop = 286 mDisplayCutout == null ? 0 : mDisplayCutout.getWaterfallInsets().top; 287 mPadding = 288 StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner( 289 mDisplayCutout, cornerCutoutMargins, mRoundedCornerPadding); 290 setPadding(mPadding.first, waterfallTop, mPadding.second, 0); 291 } 292 updateLayoutParamsNoCutout()293 private boolean updateLayoutParamsNoCutout() { 294 if (mLayoutState == LAYOUT_NO_CUTOUT) { 295 return false; 296 } 297 mLayoutState = LAYOUT_NO_CUTOUT; 298 299 if (mCutoutSpace != null) { 300 mCutoutSpace.setVisibility(View.GONE); 301 } 302 303 RelativeLayout.LayoutParams lp = (LayoutParams) mCarrierLabel.getLayoutParams(); 304 lp.addRule(RelativeLayout.START_OF, R.id.status_icon_area); 305 306 lp = (LayoutParams) mStatusIconArea.getLayoutParams(); 307 lp.removeRule(RelativeLayout.RIGHT_OF); 308 lp.width = LayoutParams.WRAP_CONTENT; 309 310 LinearLayout.LayoutParams llp = 311 (LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams(); 312 llp.setMarginStart(getResources().getDimensionPixelSize( 313 R.dimen.system_icons_super_container_margin_start)); 314 return true; 315 } 316 updateLayoutParamsForCutout()317 private boolean updateLayoutParamsForCutout() { 318 if (mLayoutState == LAYOUT_CUTOUT) { 319 return false; 320 } 321 mLayoutState = LAYOUT_CUTOUT; 322 323 if (mCutoutSpace == null) { 324 updateLayoutParamsNoCutout(); 325 } 326 327 Rect bounds = new Rect(); 328 boundsFromDirection(mDisplayCutout, Gravity.TOP, bounds); 329 330 mCutoutSpace.setVisibility(View.VISIBLE); 331 RelativeLayout.LayoutParams lp = (LayoutParams) mCutoutSpace.getLayoutParams(); 332 bounds.left = bounds.left + mCutoutSideNudge; 333 bounds.right = bounds.right - mCutoutSideNudge; 334 lp.width = bounds.width(); 335 lp.height = bounds.height(); 336 lp.addRule(RelativeLayout.CENTER_IN_PARENT); 337 338 lp = (LayoutParams) mCarrierLabel.getLayoutParams(); 339 lp.addRule(RelativeLayout.START_OF, R.id.cutout_space_view); 340 341 lp = (LayoutParams) mStatusIconArea.getLayoutParams(); 342 lp.addRule(RelativeLayout.RIGHT_OF, R.id.cutout_space_view); 343 lp.width = LayoutParams.MATCH_PARENT; 344 345 LinearLayout.LayoutParams llp = 346 (LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams(); 347 llp.setMarginStart(0); 348 return true; 349 } 350 setListening(boolean listening)351 public void setListening(boolean listening) { 352 if (listening == mBatteryListening) { 353 return; 354 } 355 mBatteryListening = listening; 356 if (mBatteryListening) { 357 mBatteryController.addCallback(this); 358 } else { 359 mBatteryController.removeCallback(this); 360 } 361 } 362 363 @Override onAttachedToWindow()364 protected void onAttachedToWindow() { 365 super.onAttachedToWindow(); 366 UserInfoController userInfoController = Dependency.get(UserInfoController.class); 367 userInfoController.addCallback(this); 368 userInfoController.reloadUserInfo(); 369 Dependency.get(ConfigurationController.class).addCallback(this); 370 mIconManager = new TintedIconManager(findViewById(R.id.statusIcons), mFeatureFlags); 371 mIconManager.setBlockList(mBlockedIcons); 372 Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager); 373 mAnimationScheduler.addCallback(this); 374 onThemeChanged(); 375 } 376 377 @Override onDetachedFromWindow()378 protected void onDetachedFromWindow() { 379 super.onDetachedFromWindow(); 380 Dependency.get(UserInfoController.class).removeCallback(this); 381 Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager); 382 Dependency.get(ConfigurationController.class).removeCallback(this); 383 mAnimationScheduler.removeCallback(this); 384 } 385 386 @Override onUserInfoChanged(String name, Drawable picture, String userAccount)387 public void onUserInfoChanged(String name, Drawable picture, String userAccount) { 388 mMultiUserAvatar.setImageDrawable(picture); 389 } 390 391 @Override onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging)392 public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { 393 if (mBatteryCharging != charging) { 394 mBatteryCharging = charging; 395 updateVisibilities(); 396 } 397 } 398 399 @Override onPowerSaveChanged(boolean isPowerSave)400 public void onPowerSaveChanged(boolean isPowerSave) { 401 // could not care less 402 } 403 setKeyguardUserSwitcherEnabled(boolean enabled)404 public void setKeyguardUserSwitcherEnabled(boolean enabled) { 405 mKeyguardUserSwitcherEnabled = enabled; 406 } 407 animateNextLayoutChange()408 private void animateNextLayoutChange() { 409 final int systemIconsCurrentX = mSystemIconsContainer.getLeft(); 410 final boolean userAvatarVisible = mMultiUserAvatar.getParent() == mStatusIconArea; 411 getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { 412 @Override 413 public boolean onPreDraw() { 414 getViewTreeObserver().removeOnPreDrawListener(this); 415 boolean userAvatarHiding = userAvatarVisible 416 && mMultiUserAvatar.getParent() != mStatusIconArea; 417 mSystemIconsContainer.setX(systemIconsCurrentX); 418 mSystemIconsContainer.animate() 419 .translationX(0) 420 .setDuration(400) 421 .setStartDelay(userAvatarHiding ? 300 : 0) 422 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) 423 .start(); 424 if (userAvatarHiding) { 425 getOverlay().add(mMultiUserAvatar); 426 mMultiUserAvatar.animate() 427 .alpha(0f) 428 .setDuration(300) 429 .setStartDelay(0) 430 .setInterpolator(Interpolators.ALPHA_OUT) 431 .withEndAction(() -> { 432 mMultiUserAvatar.setAlpha(1f); 433 getOverlay().remove(mMultiUserAvatar); 434 }) 435 .start(); 436 437 } else { 438 mMultiUserAvatar.setAlpha(0f); 439 mMultiUserAvatar.animate() 440 .alpha(1f) 441 .setDuration(300) 442 .setStartDelay(200) 443 .setInterpolator(Interpolators.ALPHA_IN); 444 } 445 return true; 446 } 447 }); 448 449 } 450 451 @Override setVisibility(int visibility)452 public void setVisibility(int visibility) { 453 super.setVisibility(visibility); 454 if (visibility != View.VISIBLE) { 455 mSystemIconsContainer.animate().cancel(); 456 mSystemIconsContainer.setTranslationX(0); 457 mMultiUserAvatar.animate().cancel(); 458 mMultiUserAvatar.setAlpha(1f); 459 } else { 460 updateVisibilities(); 461 updateSystemIconsLayoutParams(); 462 } 463 } 464 465 @Override hasOverlappingRendering()466 public boolean hasOverlappingRendering() { 467 return false; 468 } 469 onThemeChanged()470 public void onThemeChanged() { 471 mBatteryView.setColorsFromContext(mContext); 472 updateIconsAndTextColors(); 473 // Reload user avatar 474 ((UserInfoControllerImpl) Dependency.get(UserInfoController.class)) 475 .onDensityOrFontScaleChanged(); 476 } 477 478 @Override onDensityOrFontScaleChanged()479 public void onDensityOrFontScaleChanged() { 480 loadDimens(); 481 } 482 483 @Override onOverlayChanged()484 public void onOverlayChanged() { 485 mCarrierLabel.setTextAppearance( 486 Utils.getThemeAttr(mContext, com.android.internal.R.attr.textAppearanceSmall)); 487 onThemeChanged(); 488 mBatteryView.updatePercentView(); 489 } 490 updateIconsAndTextColors()491 private void updateIconsAndTextColors() { 492 @ColorInt int textColor = Utils.getColorAttrDefaultColor(mContext, 493 R.attr.wallpaperTextColor); 494 @ColorInt int iconColor = Utils.getColorStateListDefaultColor(mContext, 495 Color.luminance(textColor) < 0.5 ? R.color.dark_mode_icon_color_single_tone : 496 R.color.light_mode_icon_color_single_tone); 497 float intensity = textColor == Color.WHITE ? 0 : 1; 498 mCarrierLabel.setTextColor(iconColor); 499 if (mIconManager != null) { 500 mIconManager.setTint(iconColor); 501 } 502 503 applyDarkness(R.id.battery, mEmptyRect, intensity, iconColor); 504 applyDarkness(R.id.clock, mEmptyRect, intensity, iconColor); 505 } 506 applyDarkness(int id, Rect tintArea, float intensity, int color)507 private void applyDarkness(int id, Rect tintArea, float intensity, int color) { 508 View v = findViewById(id); 509 if (v instanceof DarkReceiver) { 510 ((DarkReceiver) v).onDarkChanged(tintArea, intensity, color); 511 } 512 } 513 514 /** 515 * Calculates the margin that isn't already accounted for in the view's padding. 516 */ calculateMargin(int margin, int padding)517 private int calculateMargin(int margin, int padding) { 518 if (padding >= margin) { 519 return 0; 520 } else { 521 return margin - padding; 522 } 523 } 524 dump(FileDescriptor fd, PrintWriter pw, String[] args)525 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 526 pw.println("KeyguardStatusBarView:"); 527 pw.println(" mBatteryCharging: " + mBatteryCharging); 528 pw.println(" mBatteryListening: " + mBatteryListening); 529 pw.println(" mLayoutState: " + mLayoutState); 530 pw.println(" mKeyguardUserSwitcherEnabled: " + mKeyguardUserSwitcherEnabled); 531 if (mBatteryView != null) { 532 mBatteryView.dump(fd, pw, args); 533 } 534 } 535 536 /** SystemStatusAnimationCallback */ 537 @Override onSystemChromeAnimationStart()538 public void onSystemChromeAnimationStart() { 539 if (mAnimationScheduler.getAnimationState() == ANIMATING_OUT) { 540 mSystemIconsContainer.setVisibility(View.VISIBLE); 541 mSystemIconsContainer.setAlpha(0f); 542 } 543 } 544 545 @Override onSystemChromeAnimationEnd()546 public void onSystemChromeAnimationEnd() { 547 // Make sure the system icons are out of the way 548 if (mAnimationScheduler.getAnimationState() == ANIMATING_IN) { 549 mSystemIconsContainer.setVisibility(View.INVISIBLE); 550 mSystemIconsContainer.setAlpha(0f); 551 } else { 552 mSystemIconsContainer.setAlpha(1f); 553 mSystemIconsContainer.setVisibility(View.VISIBLE); 554 } 555 } 556 557 @Override onSystemChromeAnimationUpdate(ValueAnimator anim)558 public void onSystemChromeAnimationUpdate(ValueAnimator anim) { 559 mSystemIconsContainer.setAlpha((float) anim.getAnimatedValue()); 560 } 561 562 @Override onLayout(boolean changed, int l, int t, int r, int b)563 protected void onLayout(boolean changed, int l, int t, int r, int b) { 564 super.onLayout(changed, l, t, r, b); 565 updateClipping(); 566 } 567 568 /** 569 * Set the clipping on the top of the view. 570 */ setTopClipping(int topClipping)571 public void setTopClipping(int topClipping) { 572 if (topClipping != mTopClipping) { 573 mTopClipping = topClipping; 574 updateClipping(); 575 } 576 } 577 updateClipping()578 private void updateClipping() { 579 mClipRect.set(0, mTopClipping, getWidth(), getHeight()); 580 setClipBounds(mClipRect); 581 } 582 } 583