1 /*
2  * Copyright (C) 2019 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.window;
18 
19 import static android.view.MotionEvent.ACTION_DOWN;
20 import static android.view.MotionEvent.ACTION_MOVE;
21 import static android.view.MotionEvent.ACTION_UP;
22 import static android.view.WindowInsets.Type.systemBars;
23 
24 import android.content.Context;
25 import android.graphics.Insets;
26 import android.util.AttributeSet;
27 import android.view.DisplayCutout;
28 import android.view.MotionEvent;
29 import android.view.View;
30 import android.view.WindowInsets;
31 import android.widget.FrameLayout;
32 
33 /**
34  * Status bar view.
35  */
36 public class StatusBarWindowView extends FrameLayout {
37 
38     public static final String TAG = "PhoneStatusBarWindowView";
39     public static final boolean DEBUG = false;
40 
41     private int mLeftInset = 0;
42     private int mRightInset = 0;
43     private int mTopInset = 0;
44 
45     private float mTouchDownY = 0;
46 
StatusBarWindowView(Context context, AttributeSet attrs)47     public StatusBarWindowView(Context context, AttributeSet attrs) {
48         super(context, attrs);
49         setClipChildren(false);
50     }
51 
52     @Override
onApplyWindowInsets(WindowInsets windowInsets)53     public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
54         final Insets insets = windowInsets.getInsetsIgnoringVisibility(systemBars());
55         mLeftInset = insets.left;
56         mRightInset = insets.right;
57         mTopInset = 0;
58         DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
59         if (displayCutout != null) {
60             mTopInset = displayCutout.getWaterfallInsets().top;
61         }
62         applyMargins();
63         return windowInsets;
64     }
65 
66     /**
67      * This is specifically for pulling down the status bar as a consistent motion in the visual
68      * immersive mode. In the visual immersive mode, after the system detects a system gesture
69      * motion from the top, we show permanent bars, and then forward the touch events from the
70      * focused window to the status bar window. However, since the first relayed event is out of
71      * bound of the status bar view, in order for the touch event to be correctly dispatched down,
72      * we jot down the position Y of the initial touch down event, offset it to 0 in the y-axis,
73      * and calculate the movement based on first touch down position.
74      */
75     @Override
dispatchTouchEvent(MotionEvent ev)76     public boolean dispatchTouchEvent(MotionEvent ev) {
77         if (ev.getAction() == ACTION_DOWN && ev.getRawY() > getHeight()) {
78             mTouchDownY = ev.getRawY();
79             ev.setLocation(ev.getRawX(), mTopInset);
80         } else if (ev.getAction() == ACTION_MOVE && mTouchDownY != 0) {
81             ev.setLocation(ev.getRawX(), mTopInset + ev.getRawY() - mTouchDownY);
82         } else if (ev.getAction() == ACTION_UP) {
83             mTouchDownY = 0;
84         }
85         return super.dispatchTouchEvent(ev);
86     }
87 
applyMargins()88     private void applyMargins() {
89         final int count = getChildCount();
90         for (int i = 0; i < count; i++) {
91             View child = getChildAt(i);
92             if (child.getLayoutParams() instanceof LayoutParams) {
93                 LayoutParams lp = (LayoutParams) child.getLayoutParams();
94                 if (lp.rightMargin != mRightInset || lp.leftMargin != mLeftInset
95                         || lp.topMargin != mTopInset) {
96                     lp.rightMargin = mRightInset;
97                     lp.leftMargin = mLeftInset;
98                     lp.topMargin = mTopInset;
99                     child.requestLayout();
100                 }
101             }
102         }
103     }
104 }
105