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.graphics.Canvas;
22 import android.util.AttributeSet;
23 import android.view.View;
24 import android.view.WindowInsets;
25 
26 import androidx.constraintlayout.widget.ConstraintLayout;
27 
28 import com.android.systemui.R;
29 import com.android.systemui.fragments.FragmentHostManager;
30 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
31 import com.android.systemui.plugins.qs.QS;
32 import com.android.systemui.statusbar.notification.AboveShelfObserver;
33 
34 import java.util.ArrayList;
35 import java.util.Comparator;
36 import java.util.function.Consumer;
37 
38 /**
39  * The container with notification stack scroller and quick settings inside.
40  */
41 public class NotificationsQuickSettingsContainer extends ConstraintLayout
42         implements FragmentListener, AboveShelfObserver.HasViewAboveShelfChangedListener {
43 
44     private View mQsFrame;
45     private View mStackScroller;
46     private View mKeyguardStatusBar;
47 
48     private int mStackScrollerMargin;
49     private ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
50     private ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
51     private final Comparator<View> mIndexComparator = Comparator.comparingInt(this::indexOfChild);
52     private Consumer<WindowInsets> mInsetsChangedListener = insets -> {};
53     private Consumer<QS> mQSFragmentAttachedListener = qs -> {};
54     private QS mQs;
55     private View mQSScrollView;
56 
NotificationsQuickSettingsContainer(Context context, AttributeSet attrs)57     public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
58         super(context, attrs);
59     }
60 
61     @Override
onFinishInflate()62     protected void onFinishInflate() {
63         super.onFinishInflate();
64         mQsFrame = findViewById(R.id.qs_frame);
65         mStackScroller = findViewById(R.id.notification_stack_scroller);
66         mStackScrollerMargin = ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin;
67         mKeyguardStatusBar = findViewById(R.id.keyguard_header);
68     }
69 
70     @Override
onFragmentViewCreated(String tag, Fragment fragment)71     public void onFragmentViewCreated(String tag, Fragment fragment) {
72         mQs = (QS) fragment;
73         mQSFragmentAttachedListener.accept(mQs);
74         mQSScrollView = mQs.getView().findViewById(R.id.expanded_qs_scroll_view);
75     }
76 
77     @Override
onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf)78     public void onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf) {
79         invalidate();
80     }
81 
setNotificationsMarginBottom(int margin)82     public void setNotificationsMarginBottom(int margin) {
83         LayoutParams params = (LayoutParams) mStackScroller.getLayoutParams();
84         params.bottomMargin = margin;
85         mStackScroller.setLayoutParams(params);
86     }
87 
setQSScrollPaddingBottom(int paddingBottom)88     public void setQSScrollPaddingBottom(int paddingBottom) {
89         if (mQSScrollView != null) {
90             mQSScrollView.setPaddingRelative(
91                     mQSScrollView.getPaddingLeft(),
92                     mQSScrollView.getPaddingTop(),
93                     mQSScrollView.getPaddingRight(),
94                     paddingBottom
95             );
96         }
97     }
98 
getDefaultNotificationsMarginBottom()99     public int getDefaultNotificationsMarginBottom() {
100         return mStackScrollerMargin;
101     }
102 
setInsetsChangedListener(Consumer<WindowInsets> onInsetsChangedListener)103     public void setInsetsChangedListener(Consumer<WindowInsets> onInsetsChangedListener) {
104         mInsetsChangedListener = onInsetsChangedListener;
105     }
106 
removeOnInsetsChangedListener()107     public void removeOnInsetsChangedListener() {
108         mInsetsChangedListener = insets -> {};
109     }
110 
setQSFragmentAttachedListener(Consumer<QS> qsFragmentAttachedListener)111     public void setQSFragmentAttachedListener(Consumer<QS> qsFragmentAttachedListener) {
112         mQSFragmentAttachedListener = qsFragmentAttachedListener;
113         // listener might be attached after fragment is attached
114         if (mQs != null) {
115             mQSFragmentAttachedListener.accept(mQs);
116         }
117     }
118 
removeQSFragmentAttachedListener()119     public void removeQSFragmentAttachedListener() {
120         mQSFragmentAttachedListener = qs -> {};
121     }
122 
123     @Override
onAttachedToWindow()124     protected void onAttachedToWindow() {
125         super.onAttachedToWindow();
126         FragmentHostManager.get(this).addTagListener(QS.TAG, this);
127     }
128 
129     @Override
onDetachedFromWindow()130     protected void onDetachedFromWindow() {
131         super.onDetachedFromWindow();
132         FragmentHostManager.get(this).removeTagListener(QS.TAG, this);
133     }
134 
135     @Override
onApplyWindowInsets(WindowInsets insets)136     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
137         mInsetsChangedListener.accept(insets);
138         return insets;
139     }
140 
141     @Override
dispatchDraw(Canvas canvas)142     protected void dispatchDraw(Canvas canvas) {
143         mDrawingOrderedChildren.clear();
144         mLayoutDrawingOrder.clear();
145         if (mKeyguardStatusBar.getVisibility() == View.VISIBLE) {
146             mDrawingOrderedChildren.add(mKeyguardStatusBar);
147             mLayoutDrawingOrder.add(mKeyguardStatusBar);
148         }
149         if (mQsFrame.getVisibility() == View.VISIBLE) {
150             mDrawingOrderedChildren.add(mQsFrame);
151             mLayoutDrawingOrder.add(mQsFrame);
152         }
153         if (mStackScroller.getVisibility() == View.VISIBLE) {
154             mDrawingOrderedChildren.add(mStackScroller);
155             mLayoutDrawingOrder.add(mStackScroller);
156         }
157 
158         // Let's now find the order that the view has when drawing regularly by sorting
159         mLayoutDrawingOrder.sort(mIndexComparator);
160         super.dispatchDraw(canvas);
161     }
162 
163     @Override
drawChild(Canvas canvas, View child, long drawingTime)164     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
165         int layoutIndex = mLayoutDrawingOrder.indexOf(child);
166         if (layoutIndex >= 0) {
167             return super.drawChild(canvas, mDrawingOrderedChildren.get(layoutIndex), drawingTime);
168         } else {
169             return super.drawChild(canvas, child, drawingTime);
170         }
171     }
172 
173 }
174