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.ScreenDecorations.DisplayCutoutView.boundsFromDirection; 20 21 import android.annotation.ColorInt; 22 import android.content.Context; 23 import android.content.res.Configuration; 24 import android.content.res.Resources; 25 import android.graphics.Color; 26 import android.graphics.Rect; 27 import android.graphics.drawable.Drawable; 28 import android.util.AttributeSet; 29 import android.util.Pair; 30 import android.util.TypedValue; 31 import android.view.DisplayCutout; 32 import android.view.Gravity; 33 import android.view.View; 34 import android.view.ViewGroup; 35 import android.view.ViewTreeObserver; 36 import android.view.WindowInsets; 37 import android.widget.ImageView; 38 import android.widget.LinearLayout; 39 import android.widget.RelativeLayout; 40 import android.widget.TextView; 41 42 import com.android.settingslib.Utils; 43 import com.android.systemui.BatteryMeterView; 44 import com.android.systemui.Dependency; 45 import com.android.systemui.Interpolators; 46 import com.android.systemui.R; 47 import com.android.systemui.qs.QSPanel; 48 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager; 49 import com.android.systemui.statusbar.policy.BatteryController; 50 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; 51 import com.android.systemui.statusbar.policy.ConfigurationController; 52 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; 53 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; 54 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; 55 import com.android.systemui.statusbar.policy.UserInfoController; 56 import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener; 57 import com.android.systemui.statusbar.policy.UserInfoControllerImpl; 58 import com.android.systemui.statusbar.policy.UserSwitcherController; 59 60 /** 61 * The header group on Keyguard. 62 */ 63 public class KeyguardStatusBarView extends RelativeLayout 64 implements BatteryStateChangeCallback, OnUserInfoChangedListener, ConfigurationListener { 65 66 private static final int LAYOUT_NONE = 0; 67 private static final int LAYOUT_CUTOUT = 1; 68 private static final int LAYOUT_NO_CUTOUT = 2; 69 70 private boolean mBatteryCharging; 71 private boolean mKeyguardUserSwitcherShowing; 72 private boolean mBatteryListening; 73 74 private TextView mCarrierLabel; 75 private View mSystemIconsSuperContainer; 76 private MultiUserSwitch mMultiUserSwitch; 77 private ImageView mMultiUserAvatar; 78 private BatteryMeterView mBatteryView; 79 80 private BatteryController mBatteryController; 81 private KeyguardUserSwitcher mKeyguardUserSwitcher; 82 private UserSwitcherController mUserSwitcherController; 83 84 private int mSystemIconsSwitcherHiddenExpandedMargin; 85 private int mSystemIconsBaseMargin; 86 private View mSystemIconsContainer; 87 private TintedIconManager mIconManager; 88 89 private View mCutoutSpace; 90 private ViewGroup mStatusIconArea; 91 private int mLayoutState = LAYOUT_NONE; 92 93 /** 94 * Draw this many pixels into the left/right side of the cutout to optimally use the space 95 */ 96 private int mCutoutSideNudge = 0; 97 KeyguardStatusBarView(Context context, AttributeSet attrs)98 public KeyguardStatusBarView(Context context, AttributeSet attrs) { 99 super(context, attrs); 100 } 101 102 @Override onFinishInflate()103 protected void onFinishInflate() { 104 super.onFinishInflate(); 105 mSystemIconsContainer = findViewById(R.id.system_icons_container); 106 mMultiUserSwitch = findViewById(R.id.multi_user_switch); 107 mMultiUserAvatar = findViewById(R.id.multi_user_avatar); 108 mCarrierLabel = findViewById(R.id.keyguard_carrier_text); 109 mBatteryView = mSystemIconsContainer.findViewById(R.id.battery); 110 mCutoutSpace = findViewById(R.id.cutout_space_view); 111 mStatusIconArea = findViewById(R.id.status_icon_area); 112 113 loadDimens(); 114 updateUserSwitcher(); 115 mBatteryController = Dependency.get(BatteryController.class); 116 } 117 118 @Override onConfigurationChanged(Configuration newConfig)119 protected void onConfigurationChanged(Configuration newConfig) { 120 super.onConfigurationChanged(newConfig); 121 122 MarginLayoutParams lp = (MarginLayoutParams) mMultiUserAvatar.getLayoutParams(); 123 lp.width = lp.height = getResources().getDimensionPixelSize( 124 R.dimen.multi_user_avatar_keyguard_size); 125 mMultiUserAvatar.setLayoutParams(lp); 126 127 // Multi-user switch 128 lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams(); 129 lp.width = getResources().getDimensionPixelSize( 130 R.dimen.multi_user_switch_width_keyguard); 131 lp.setMarginEnd(getResources().getDimensionPixelSize( 132 R.dimen.multi_user_switch_keyguard_margin)); 133 mMultiUserSwitch.setLayoutParams(lp); 134 135 // System icons 136 lp = (MarginLayoutParams) mSystemIconsContainer.getLayoutParams(); 137 lp.setMarginStart(getResources().getDimensionPixelSize( 138 R.dimen.system_icons_super_container_margin_start)); 139 mSystemIconsContainer.setLayoutParams(lp); 140 mSystemIconsContainer.setPaddingRelative(mSystemIconsContainer.getPaddingStart(), 141 mSystemIconsContainer.getPaddingTop(), 142 getResources().getDimensionPixelSize(R.dimen.system_icons_keyguard_padding_end), 143 mSystemIconsContainer.getPaddingBottom()); 144 145 // Respect font size setting. 146 mCarrierLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, 147 getResources().getDimensionPixelSize( 148 com.android.internal.R.dimen.text_size_small_material)); 149 lp = (MarginLayoutParams) mCarrierLabel.getLayoutParams(); 150 lp.setMarginStart( 151 getResources().getDimensionPixelSize(R.dimen.keyguard_carrier_text_margin)); 152 mCarrierLabel.setLayoutParams(lp); 153 154 lp = (MarginLayoutParams) getLayoutParams(); 155 lp.height = getResources().getDimensionPixelSize( 156 R.dimen.status_bar_header_height_keyguard); 157 setLayoutParams(lp); 158 } 159 loadDimens()160 private void loadDimens() { 161 Resources res = getResources(); 162 mSystemIconsSwitcherHiddenExpandedMargin = res.getDimensionPixelSize( 163 R.dimen.system_icons_switcher_hidden_expanded_margin); 164 mSystemIconsBaseMargin = res.getDimensionPixelSize( 165 R.dimen.system_icons_super_container_avatarless_margin_end); 166 mCutoutSideNudge = getResources().getDimensionPixelSize( 167 R.dimen.display_cutout_margin_consumption); 168 } 169 updateVisibilities()170 private void updateVisibilities() { 171 if (mMultiUserSwitch.getParent() != mStatusIconArea && !mKeyguardUserSwitcherShowing) { 172 if (mMultiUserSwitch.getParent() != null) { 173 getOverlay().remove(mMultiUserSwitch); 174 } 175 mStatusIconArea.addView(mMultiUserSwitch, 0); 176 } else if (mMultiUserSwitch.getParent() == mStatusIconArea && mKeyguardUserSwitcherShowing) { 177 mStatusIconArea.removeView(mMultiUserSwitch); 178 } 179 if (mKeyguardUserSwitcher == null) { 180 // If we have no keyguard switcher, the screen width is under 600dp. In this case, 181 // we don't show the multi-user avatar unless there is more than 1 user on the device. 182 if (mUserSwitcherController != null 183 && mUserSwitcherController.getSwitchableUserCount() > 1) { 184 mMultiUserSwitch.setVisibility(View.VISIBLE); 185 } else { 186 mMultiUserSwitch.setVisibility(View.GONE); 187 } 188 } 189 mBatteryView.setForceShowPercent(mBatteryCharging); 190 } 191 updateSystemIconsLayoutParams()192 private void updateSystemIconsLayoutParams() { 193 LinearLayout.LayoutParams lp = 194 (LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams(); 195 // If the avatar icon is gone, we need to have some end margin to display the system icons 196 // correctly. 197 int baseMarginEnd = mMultiUserSwitch.getVisibility() == View.GONE 198 ? mSystemIconsBaseMargin 199 : 0; 200 int marginEnd = mKeyguardUserSwitcherShowing ? mSystemIconsSwitcherHiddenExpandedMargin : 201 baseMarginEnd; 202 if (marginEnd != lp.getMarginEnd()) { 203 lp.setMarginEnd(marginEnd); 204 mSystemIconsContainer.setLayoutParams(lp); 205 } 206 } 207 208 @Override onApplyWindowInsets(WindowInsets insets)209 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 210 mLayoutState = LAYOUT_NONE; 211 if (updateLayoutConsideringCutout()) { 212 requestLayout(); 213 } 214 return super.onApplyWindowInsets(insets); 215 } 216 updateLayoutConsideringCutout()217 private boolean updateLayoutConsideringCutout() { 218 DisplayCutout dc = getRootWindowInsets().getDisplayCutout(); 219 Pair<Integer, Integer> cornerCutoutMargins = 220 PhoneStatusBarView.cornerCutoutMargins(dc, getDisplay()); 221 updateCornerCutoutPadding(cornerCutoutMargins); 222 if (dc == null || cornerCutoutMargins != null) { 223 return updateLayoutParamsNoCutout(); 224 } else { 225 return updateLayoutParamsForCutout(dc); 226 } 227 } 228 updateCornerCutoutPadding(Pair<Integer, Integer> cornerCutoutMargins)229 private void updateCornerCutoutPadding(Pair<Integer, Integer> cornerCutoutMargins) { 230 if (cornerCutoutMargins != null) { 231 setPadding(cornerCutoutMargins.first, 0, cornerCutoutMargins.second, 0); 232 } else { 233 setPadding(0, 0, 0, 0); 234 } 235 } 236 updateLayoutParamsNoCutout()237 private boolean updateLayoutParamsNoCutout() { 238 if (mLayoutState == LAYOUT_NO_CUTOUT) { 239 return false; 240 } 241 mLayoutState = LAYOUT_NO_CUTOUT; 242 243 if (mCutoutSpace != null) { 244 mCutoutSpace.setVisibility(View.GONE); 245 } 246 247 RelativeLayout.LayoutParams lp = (LayoutParams) mCarrierLabel.getLayoutParams(); 248 lp.addRule(RelativeLayout.START_OF, R.id.status_icon_area); 249 250 lp = (LayoutParams) mStatusIconArea.getLayoutParams(); 251 lp.removeRule(RelativeLayout.RIGHT_OF); 252 lp.width = LayoutParams.WRAP_CONTENT; 253 254 LinearLayout.LayoutParams llp = 255 (LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams(); 256 llp.setMarginStart(getResources().getDimensionPixelSize( 257 R.dimen.system_icons_super_container_margin_start)); 258 return true; 259 } 260 updateLayoutParamsForCutout(DisplayCutout dc)261 private boolean updateLayoutParamsForCutout(DisplayCutout dc) { 262 if (mLayoutState == LAYOUT_CUTOUT) { 263 return false; 264 } 265 mLayoutState = LAYOUT_CUTOUT; 266 267 if (mCutoutSpace == null) { 268 updateLayoutParamsNoCutout(); 269 } 270 271 Rect bounds = new Rect(); 272 boundsFromDirection(dc, Gravity.TOP, bounds); 273 274 mCutoutSpace.setVisibility(View.VISIBLE); 275 RelativeLayout.LayoutParams lp = (LayoutParams) mCutoutSpace.getLayoutParams(); 276 bounds.left = bounds.left + mCutoutSideNudge; 277 bounds.right = bounds.right - mCutoutSideNudge; 278 lp.width = bounds.width(); 279 lp.height = bounds.height(); 280 lp.addRule(RelativeLayout.CENTER_IN_PARENT); 281 282 lp = (LayoutParams) mCarrierLabel.getLayoutParams(); 283 lp.addRule(RelativeLayout.START_OF, R.id.cutout_space_view); 284 285 lp = (LayoutParams) mStatusIconArea.getLayoutParams(); 286 lp.addRule(RelativeLayout.RIGHT_OF, R.id.cutout_space_view); 287 lp.width = LayoutParams.MATCH_PARENT; 288 289 LinearLayout.LayoutParams llp = 290 (LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams(); 291 llp.setMarginStart(0); 292 return true; 293 } 294 setListening(boolean listening)295 public void setListening(boolean listening) { 296 if (listening == mBatteryListening) { 297 return; 298 } 299 mBatteryListening = listening; 300 if (mBatteryListening) { 301 mBatteryController.addCallback(this); 302 } else { 303 mBatteryController.removeCallback(this); 304 } 305 } 306 updateUserSwitcher()307 private void updateUserSwitcher() { 308 boolean keyguardSwitcherAvailable = mKeyguardUserSwitcher != null; 309 mMultiUserSwitch.setClickable(keyguardSwitcherAvailable); 310 mMultiUserSwitch.setFocusable(keyguardSwitcherAvailable); 311 mMultiUserSwitch.setKeyguardMode(keyguardSwitcherAvailable); 312 } 313 314 @Override onAttachedToWindow()315 protected void onAttachedToWindow() { 316 super.onAttachedToWindow(); 317 UserInfoController userInfoController = Dependency.get(UserInfoController.class); 318 userInfoController.addCallback(this); 319 mUserSwitcherController = Dependency.get(UserSwitcherController.class); 320 mMultiUserSwitch.setUserSwitcherController(mUserSwitcherController); 321 userInfoController.reloadUserInfo(); 322 Dependency.get(ConfigurationController.class).addCallback(this); 323 mIconManager = new TintedIconManager(findViewById(R.id.statusIcons)); 324 Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager); 325 onThemeChanged(); 326 } 327 328 @Override onDetachedFromWindow()329 protected void onDetachedFromWindow() { 330 super.onDetachedFromWindow(); 331 Dependency.get(UserInfoController.class).removeCallback(this); 332 Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager); 333 Dependency.get(ConfigurationController.class).removeCallback(this); 334 } 335 336 @Override onUserInfoChanged(String name, Drawable picture, String userAccount)337 public void onUserInfoChanged(String name, Drawable picture, String userAccount) { 338 mMultiUserAvatar.setImageDrawable(picture); 339 } 340 setQSPanel(QSPanel qsp)341 public void setQSPanel(QSPanel qsp) { 342 mMultiUserSwitch.setQsPanel(qsp); 343 } 344 345 @Override onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging)346 public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { 347 if (mBatteryCharging != charging) { 348 mBatteryCharging = charging; 349 updateVisibilities(); 350 } 351 } 352 353 @Override onPowerSaveChanged(boolean isPowerSave)354 public void onPowerSaveChanged(boolean isPowerSave) { 355 // could not care less 356 } 357 setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher)358 public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) { 359 mKeyguardUserSwitcher = keyguardUserSwitcher; 360 mMultiUserSwitch.setKeyguardUserSwitcher(keyguardUserSwitcher); 361 updateUserSwitcher(); 362 } 363 setKeyguardUserSwitcherShowing(boolean showing, boolean animate)364 public void setKeyguardUserSwitcherShowing(boolean showing, boolean animate) { 365 mKeyguardUserSwitcherShowing = showing; 366 if (animate) { 367 animateNextLayoutChange(); 368 } 369 updateVisibilities(); 370 updateLayoutConsideringCutout(); 371 updateSystemIconsLayoutParams(); 372 } 373 animateNextLayoutChange()374 private void animateNextLayoutChange() { 375 final int systemIconsCurrentX = mSystemIconsContainer.getLeft(); 376 final boolean userSwitcherVisible = mMultiUserSwitch.getParent() == mStatusIconArea; 377 getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { 378 @Override 379 public boolean onPreDraw() { 380 getViewTreeObserver().removeOnPreDrawListener(this); 381 boolean userSwitcherHiding = userSwitcherVisible 382 && mMultiUserSwitch.getParent() != mStatusIconArea; 383 mSystemIconsContainer.setX(systemIconsCurrentX); 384 mSystemIconsContainer.animate() 385 .translationX(0) 386 .setDuration(400) 387 .setStartDelay(userSwitcherHiding ? 300 : 0) 388 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) 389 .start(); 390 if (userSwitcherHiding) { 391 getOverlay().add(mMultiUserSwitch); 392 mMultiUserSwitch.animate() 393 .alpha(0f) 394 .setDuration(300) 395 .setStartDelay(0) 396 .setInterpolator(Interpolators.ALPHA_OUT) 397 .withEndAction(() -> { 398 mMultiUserSwitch.setAlpha(1f); 399 getOverlay().remove(mMultiUserSwitch); 400 }) 401 .start(); 402 403 } else { 404 mMultiUserSwitch.setAlpha(0f); 405 mMultiUserSwitch.animate() 406 .alpha(1f) 407 .setDuration(300) 408 .setStartDelay(200) 409 .setInterpolator(Interpolators.ALPHA_IN); 410 } 411 return true; 412 } 413 }); 414 415 } 416 417 @Override setVisibility(int visibility)418 public void setVisibility(int visibility) { 419 super.setVisibility(visibility); 420 if (visibility != View.VISIBLE) { 421 mSystemIconsContainer.animate().cancel(); 422 mSystemIconsContainer.setTranslationX(0); 423 mMultiUserSwitch.animate().cancel(); 424 mMultiUserSwitch.setAlpha(1f); 425 } else { 426 updateVisibilities(); 427 updateSystemIconsLayoutParams(); 428 } 429 } 430 431 @Override hasOverlappingRendering()432 public boolean hasOverlappingRendering() { 433 return false; 434 } 435 onThemeChanged()436 public void onThemeChanged() { 437 @ColorInt int textColor = Utils.getColorAttr(mContext, R.attr.wallpaperTextColor); 438 @ColorInt int iconColor = Utils.getDefaultColor(mContext, Color.luminance(textColor) < 0.5 ? 439 R.color.dark_mode_icon_color_single_tone : 440 R.color.light_mode_icon_color_single_tone); 441 float intensity = textColor == Color.WHITE ? 0 : 1; 442 mCarrierLabel.setTextColor(iconColor); 443 mBatteryView.setFillColor(iconColor); 444 mIconManager.setTint(iconColor); 445 Rect tintArea = new Rect(0, 0, 0, 0); 446 447 applyDarkness(R.id.battery, tintArea, intensity, iconColor); 448 applyDarkness(R.id.clock, tintArea, intensity, iconColor); 449 // Reload user avatar 450 ((UserInfoControllerImpl) Dependency.get(UserInfoController.class)) 451 .onDensityOrFontScaleChanged(); 452 } 453 applyDarkness(int id, Rect tintArea, float intensity, int color)454 private void applyDarkness(int id, Rect tintArea, float intensity, int color) { 455 View v = findViewById(id); 456 if (v instanceof DarkReceiver) { 457 ((DarkReceiver) v).onDarkChanged(tintArea, intensity, color); 458 } 459 } 460 } 461