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 android.app.Fragment; 20 import android.content.Context; 21 import android.content.res.Configuration; 22 import android.graphics.Canvas; 23 import android.util.AttributeSet; 24 import android.view.View; 25 import android.view.ViewStub; 26 import android.view.ViewStub.OnInflateListener; 27 import android.view.WindowInsets; 28 import android.widget.FrameLayout; 29 30 import androidx.annotation.DimenRes; 31 32 import com.android.systemui.R; 33 import com.android.systemui.fragments.FragmentHostManager; 34 import com.android.systemui.fragments.FragmentHostManager.FragmentListener; 35 import com.android.systemui.plugins.qs.QS; 36 import com.android.systemui.statusbar.notification.AboveShelfObserver; 37 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; 38 39 /** 40 * The container with notification stack scroller and quick settings inside. 41 */ 42 public class NotificationsQuickSettingsContainer extends FrameLayout 43 implements OnInflateListener, FragmentListener, 44 AboveShelfObserver.HasViewAboveShelfChangedListener { 45 46 private FrameLayout mQsFrame; 47 private View mUserSwitcher; 48 private NotificationStackScrollLayout mStackScroller; 49 private View mKeyguardStatusBar; 50 private boolean mInflated; 51 private boolean mQsExpanded; 52 private boolean mCustomizerAnimating; 53 54 private int mBottomPadding; 55 private int mStackScrollerMargin; 56 private boolean mHasViewsAboveShelf; 57 NotificationsQuickSettingsContainer(Context context, AttributeSet attrs)58 public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) { 59 super(context, attrs); 60 } 61 62 @Override onFinishInflate()63 protected void onFinishInflate() { 64 super.onFinishInflate(); 65 mQsFrame = (FrameLayout) findViewById(R.id.qs_frame); 66 mStackScroller = findViewById(R.id.notification_stack_scroller); 67 mStackScrollerMargin = ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin; 68 mKeyguardStatusBar = findViewById(R.id.keyguard_header); 69 ViewStub userSwitcher = (ViewStub) findViewById(R.id.keyguard_user_switcher); 70 userSwitcher.setOnInflateListener(this); 71 mUserSwitcher = userSwitcher; 72 } 73 74 @Override onAttachedToWindow()75 protected void onAttachedToWindow() { 76 super.onAttachedToWindow(); 77 FragmentHostManager.get(this).addTagListener(QS.TAG, this); 78 } 79 80 @Override onDetachedFromWindow()81 protected void onDetachedFromWindow() { 82 super.onDetachedFromWindow(); 83 FragmentHostManager.get(this).removeTagListener(QS.TAG, this); 84 } 85 86 @Override onConfigurationChanged(Configuration newConfig)87 protected void onConfigurationChanged(Configuration newConfig) { 88 super.onConfigurationChanged(newConfig); 89 reloadWidth(mQsFrame, R.dimen.qs_panel_width); 90 reloadWidth(mStackScroller, R.dimen.notification_panel_width); 91 } 92 93 /** 94 * Loads the given width resource and sets it on the given View. 95 */ reloadWidth(View view, @DimenRes int width)96 private void reloadWidth(View view, @DimenRes int width) { 97 LayoutParams params = (LayoutParams) view.getLayoutParams(); 98 params.width = getResources().getDimensionPixelSize(width); 99 view.setLayoutParams(params); 100 } 101 102 @Override onApplyWindowInsets(WindowInsets insets)103 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 104 mBottomPadding = insets.getStableInsetBottom(); 105 setPadding(0, 0, 0, mBottomPadding); 106 return insets; 107 } 108 109 @Override drawChild(Canvas canvas, View child, long drawingTime)110 protected boolean drawChild(Canvas canvas, View child, long drawingTime) { 111 boolean userSwitcherVisible = mInflated && mUserSwitcher.getVisibility() == View.VISIBLE; 112 boolean statusBarVisible = mKeyguardStatusBar.getVisibility() == View.VISIBLE; 113 114 final boolean qsBottom = mHasViewsAboveShelf; 115 View stackQsTop = qsBottom ? mStackScroller : mQsFrame; 116 View stackQsBottom = !qsBottom ? mStackScroller : mQsFrame; 117 // Invert the order of the scroll view and user switcher such that the notifications receive 118 // touches first but the panel gets drawn above. 119 if (child == mQsFrame) { 120 return super.drawChild(canvas, userSwitcherVisible && statusBarVisible ? mUserSwitcher 121 : statusBarVisible ? mKeyguardStatusBar 122 : userSwitcherVisible ? mUserSwitcher 123 : stackQsBottom, drawingTime); 124 } else if (child == mStackScroller) { 125 return super.drawChild(canvas, 126 userSwitcherVisible && statusBarVisible ? mKeyguardStatusBar 127 : statusBarVisible || userSwitcherVisible ? stackQsBottom 128 : stackQsTop, 129 drawingTime); 130 } else if (child == mUserSwitcher) { 131 return super.drawChild(canvas, 132 userSwitcherVisible && statusBarVisible ? stackQsBottom 133 : stackQsTop, 134 drawingTime); 135 } else if (child == mKeyguardStatusBar) { 136 return super.drawChild(canvas, 137 stackQsTop, 138 drawingTime); 139 } else { 140 return super.drawChild(canvas, child, drawingTime); 141 } 142 } 143 144 @Override onInflate(ViewStub stub, View inflated)145 public void onInflate(ViewStub stub, View inflated) { 146 if (stub == mUserSwitcher) { 147 mUserSwitcher = inflated; 148 mInflated = true; 149 } 150 } 151 152 @Override onFragmentViewCreated(String tag, Fragment fragment)153 public void onFragmentViewCreated(String tag, Fragment fragment) { 154 QS container = (QS) fragment; 155 container.setContainer(this); 156 } 157 setQsExpanded(boolean expanded)158 public void setQsExpanded(boolean expanded) { 159 if (mQsExpanded != expanded) { 160 mQsExpanded = expanded; 161 invalidate(); 162 } 163 } 164 setCustomizerAnimating(boolean isAnimating)165 public void setCustomizerAnimating(boolean isAnimating) { 166 if (mCustomizerAnimating != isAnimating) { 167 mCustomizerAnimating = isAnimating; 168 invalidate(); 169 } 170 } 171 setCustomizerShowing(boolean isShowing)172 public void setCustomizerShowing(boolean isShowing) { 173 if (isShowing) { 174 // Clear out bottom paddings/margins so the qs customization can be full height. 175 setPadding(0, 0, 0, 0); 176 setBottomMargin(mStackScroller, 0); 177 } else { 178 setPadding(0, 0, 0, mBottomPadding); 179 setBottomMargin(mStackScroller, mStackScrollerMargin); 180 } 181 mStackScroller.setQsCustomizerShowing(isShowing); 182 } 183 setBottomMargin(View v, int bottomMargin)184 private void setBottomMargin(View v, int bottomMargin) { 185 LayoutParams params = (LayoutParams) v.getLayoutParams(); 186 params.bottomMargin = bottomMargin; 187 v.setLayoutParams(params); 188 } 189 190 @Override onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf)191 public void onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf) { 192 mHasViewsAboveShelf = hasViewsAboveShelf; 193 invalidate(); 194 } 195 } 196