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