Home | History | Annotate | Download | only in phone
      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 
     98     public KeyguardStatusBarView(Context context, AttributeSet attrs) {
     99         super(context, attrs);
    100     }
    101 
    102     @Override
    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
    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 
    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 
    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 
    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
    209     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
    210         mLayoutState = LAYOUT_NONE;
    211         if (updateLayoutConsideringCutout()) {
    212             requestLayout();
    213         }
    214         return super.onApplyWindowInsets(insets);
    215     }
    216 
    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 
    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 
    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 
    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 
    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 
    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
    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
    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
    337     public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
    338         mMultiUserAvatar.setImageDrawable(picture);
    339     }
    340 
    341     public void setQSPanel(QSPanel qsp) {
    342         mMultiUserSwitch.setQsPanel(qsp);
    343     }
    344 
    345     @Override
    346     public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
    347         if (mBatteryCharging != charging) {
    348             mBatteryCharging = charging;
    349             updateVisibilities();
    350         }
    351     }
    352 
    353     @Override
    354     public void onPowerSaveChanged(boolean isPowerSave) {
    355         // could not care less
    356     }
    357 
    358     public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
    359         mKeyguardUserSwitcher = keyguardUserSwitcher;
    360         mMultiUserSwitch.setKeyguardUserSwitcher(keyguardUserSwitcher);
    361         updateUserSwitcher();
    362     }
    363 
    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 
    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
    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
    432     public boolean hasOverlappingRendering() {
    433         return false;
    434     }
    435 
    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 
    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