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 import java.util.ArrayList; 40 import java.util.Comparator; 41 42 /** 43 * The container with notification stack scroller and quick settings inside. 44 */ 45 public class NotificationsQuickSettingsContainer extends FrameLayout 46 implements OnInflateListener, FragmentListener, 47 AboveShelfObserver.HasViewAboveShelfChangedListener { 48 49 private FrameLayout mQsFrame; 50 private View mUserSwitcher; 51 private NotificationStackScrollLayout mStackScroller; 52 private View mKeyguardStatusBar; 53 private boolean mInflated; 54 private boolean mQsExpanded; 55 private boolean mCustomizerAnimating; 56 57 private int mBottomPadding; 58 private int mStackScrollerMargin; 59 private boolean mHasViewsAboveShelf; 60 private ArrayList<View> mDrawingOrderedChildren = new ArrayList<>(); 61 private ArrayList<View> mLayoutDrawingOrder = new ArrayList<>(); 62 private final Comparator<View> mIndexComparator = Comparator.comparingInt(this::indexOfChild); 63 NotificationsQuickSettingsContainer(Context context, AttributeSet attrs)64 public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) { 65 super(context, attrs); 66 } 67 68 @Override onFinishInflate()69 protected void onFinishInflate() { 70 super.onFinishInflate(); 71 mQsFrame = (FrameLayout) findViewById(R.id.qs_frame); 72 mStackScroller = findViewById(R.id.notification_stack_scroller); 73 mStackScrollerMargin = ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin; 74 mKeyguardStatusBar = findViewById(R.id.keyguard_header); 75 ViewStub userSwitcher = (ViewStub) findViewById(R.id.keyguard_user_switcher); 76 userSwitcher.setOnInflateListener(this); 77 mUserSwitcher = userSwitcher; 78 } 79 80 @Override onAttachedToWindow()81 protected void onAttachedToWindow() { 82 super.onAttachedToWindow(); 83 FragmentHostManager.get(this).addTagListener(QS.TAG, this); 84 } 85 86 @Override onDetachedFromWindow()87 protected void onDetachedFromWindow() { 88 super.onDetachedFromWindow(); 89 FragmentHostManager.get(this).removeTagListener(QS.TAG, this); 90 } 91 92 @Override onConfigurationChanged(Configuration newConfig)93 protected void onConfigurationChanged(Configuration newConfig) { 94 super.onConfigurationChanged(newConfig); 95 reloadWidth(mQsFrame, R.dimen.qs_panel_width); 96 reloadWidth(mStackScroller, R.dimen.notification_panel_width); 97 } 98 99 /** 100 * Loads the given width resource and sets it on the given View. 101 */ reloadWidth(View view, @DimenRes int width)102 private void reloadWidth(View view, @DimenRes int width) { 103 LayoutParams params = (LayoutParams) view.getLayoutParams(); 104 params.width = getResources().getDimensionPixelSize(width); 105 view.setLayoutParams(params); 106 } 107 108 @Override onApplyWindowInsets(WindowInsets insets)109 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 110 mBottomPadding = insets.getStableInsetBottom(); 111 setPadding(0, 0, 0, mBottomPadding); 112 return insets; 113 } 114 115 @Override dispatchDraw(Canvas canvas)116 protected void dispatchDraw(Canvas canvas) { 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 mDrawingOrderedChildren.clear(); 120 mLayoutDrawingOrder.clear(); 121 if (mInflated && mUserSwitcher.getVisibility() == View.VISIBLE) { 122 mDrawingOrderedChildren.add(mUserSwitcher); 123 mLayoutDrawingOrder.add(mUserSwitcher); 124 } 125 if (mKeyguardStatusBar.getVisibility() == View.VISIBLE) { 126 mDrawingOrderedChildren.add(mKeyguardStatusBar); 127 mLayoutDrawingOrder.add(mKeyguardStatusBar); 128 } 129 if (mStackScroller.getVisibility() == View.VISIBLE) { 130 mDrawingOrderedChildren.add(mStackScroller); 131 mLayoutDrawingOrder.add(mStackScroller); 132 } 133 if (mQsFrame.getVisibility() == View.VISIBLE) { 134 mDrawingOrderedChildren.add(mQsFrame); 135 mLayoutDrawingOrder.add(mQsFrame); 136 } 137 138 if (mHasViewsAboveShelf) { 139 // StackScroller needs to be on top 140 mDrawingOrderedChildren.remove(mStackScroller); 141 mDrawingOrderedChildren.add(mStackScroller); 142 } 143 144 // Let's now find the order that the view has when drawing regulary by sorting 145 mLayoutDrawingOrder.sort(mIndexComparator); 146 super.dispatchDraw(canvas); 147 } 148 149 @Override drawChild(Canvas canvas, View child, long drawingTime)150 protected boolean drawChild(Canvas canvas, View child, long drawingTime) { 151 int layoutIndex = mLayoutDrawingOrder.indexOf(child); 152 if (layoutIndex >= 0) { 153 return super.drawChild(canvas, mDrawingOrderedChildren.get(layoutIndex), drawingTime); 154 } else { 155 return super.drawChild(canvas, child, drawingTime); 156 } 157 } 158 159 @Override onInflate(ViewStub stub, View inflated)160 public void onInflate(ViewStub stub, View inflated) { 161 if (stub == mUserSwitcher) { 162 mUserSwitcher = inflated; 163 mInflated = true; 164 } 165 } 166 167 @Override onFragmentViewCreated(String tag, Fragment fragment)168 public void onFragmentViewCreated(String tag, Fragment fragment) { 169 QS container = (QS) fragment; 170 container.setContainer(this); 171 } 172 setQsExpanded(boolean expanded)173 public void setQsExpanded(boolean expanded) { 174 if (mQsExpanded != expanded) { 175 mQsExpanded = expanded; 176 invalidate(); 177 } 178 } 179 setCustomizerAnimating(boolean isAnimating)180 public void setCustomizerAnimating(boolean isAnimating) { 181 if (mCustomizerAnimating != isAnimating) { 182 mCustomizerAnimating = isAnimating; 183 invalidate(); 184 } 185 } 186 setCustomizerShowing(boolean isShowing)187 public void setCustomizerShowing(boolean isShowing) { 188 if (isShowing) { 189 // Clear out bottom paddings/margins so the qs customization can be full height. 190 setPadding(0, 0, 0, 0); 191 setBottomMargin(mStackScroller, 0); 192 } else { 193 setPadding(0, 0, 0, mBottomPadding); 194 setBottomMargin(mStackScroller, mStackScrollerMargin); 195 } 196 mStackScroller.setQsCustomizerShowing(isShowing); 197 } 198 setBottomMargin(View v, int bottomMargin)199 private void setBottomMargin(View v, int bottomMargin) { 200 LayoutParams params = (LayoutParams) v.getLayoutParams(); 201 params.bottomMargin = bottomMargin; 202 v.setLayoutParams(params); 203 } 204 205 @Override onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf)206 public void onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf) { 207 mHasViewsAboveShelf = hasViewsAboveShelf; 208 invalidate(); 209 } 210 } 211