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.phone; 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 static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection; 25 26 import android.content.Context; 27 import android.graphics.Insets; 28 import android.graphics.Point; 29 import android.graphics.Rect; 30 import android.util.AttributeSet; 31 import android.util.Pair; 32 import android.view.Display; 33 import android.view.DisplayCutout; 34 import android.view.Gravity; 35 import android.view.MotionEvent; 36 import android.view.View; 37 import android.view.WindowInsets; 38 import android.widget.FrameLayout; 39 40 import androidx.annotation.NonNull; 41 42 import com.android.systemui.util.leak.RotationUtils; 43 44 /** 45 * Status bar view. 46 */ 47 public class StatusBarWindowView extends FrameLayout { 48 49 public static final String TAG = "PhoneStatusBarWindowView"; 50 public static final boolean DEBUG = StatusBar.DEBUG; 51 52 private int mLeftInset = 0; 53 private int mRightInset = 0; 54 private int mTopInset = 0; 55 56 private float mTouchDownY = 0; 57 StatusBarWindowView(Context context, AttributeSet attrs)58 public StatusBarWindowView(Context context, AttributeSet attrs) { 59 super(context, attrs); 60 } 61 62 @Override onApplyWindowInsets(WindowInsets windowInsets)63 public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) { 64 final Insets insets = windowInsets.getInsetsIgnoringVisibility(systemBars()); 65 mLeftInset = insets.left; 66 mRightInset = insets.right; 67 mTopInset = 0; 68 DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout(); 69 if (displayCutout != null) { 70 mTopInset = displayCutout.getWaterfallInsets().top; 71 } 72 applyMargins(); 73 return windowInsets; 74 } 75 76 /** 77 * This is specifically for pulling down the status bar as a consistent motion in the visual 78 * immersive mode. In the visual immersive mode, after the system detects a system gesture 79 * motion from the top, we show permanent bars, and then forward the touch events from the 80 * focused window to the status bar window. However, since the first relayed event is out of 81 * bound of the status bar view, in order for the touch event to be correctly dispatched down, 82 * we jot down the position Y of the initial touch down event, offset it to 0 in the y-axis, 83 * and calculate the movement based on first touch down position. 84 */ 85 @Override dispatchTouchEvent(MotionEvent ev)86 public boolean dispatchTouchEvent(MotionEvent ev) { 87 if (ev.getAction() == ACTION_DOWN && ev.getRawY() > getHeight()) { 88 mTouchDownY = ev.getRawY(); 89 ev.setLocation(ev.getRawX(), mTopInset); 90 } else if (ev.getAction() == ACTION_MOVE && mTouchDownY != 0) { 91 ev.setLocation(ev.getRawX(), mTopInset + ev.getRawY() - mTouchDownY); 92 } else if (ev.getAction() == ACTION_UP) { 93 mTouchDownY = 0; 94 } 95 return super.dispatchTouchEvent(ev); 96 } 97 applyMargins()98 private void applyMargins() { 99 final int count = getChildCount(); 100 for (int i = 0; i < count; i++) { 101 View child = getChildAt(i); 102 if (child.getLayoutParams() instanceof LayoutParams) { 103 LayoutParams lp = (LayoutParams) child.getLayoutParams(); 104 if (lp.rightMargin != mRightInset || lp.leftMargin != mLeftInset 105 || lp.topMargin != mTopInset) { 106 lp.rightMargin = mRightInset; 107 lp.leftMargin = mLeftInset; 108 lp.topMargin = mTopInset; 109 child.requestLayout(); 110 } 111 } 112 } 113 } 114 115 /** 116 * Compute the padding needed for status bar related views, e.g., PhoneStatusBar, 117 * QuickStatusBarHeader and KeyguardStatusBarView). 118 * 119 * @param cutout 120 * @param cornerCutoutPadding 121 * @param roundedCornerContentPadding 122 * @return 123 */ 124 @NonNull paddingNeededForCutoutAndRoundedCorner( DisplayCutout cutout, Pair<Integer, Integer> cornerCutoutPadding, int roundedCornerContentPadding)125 public static Pair<Integer, Integer> paddingNeededForCutoutAndRoundedCorner( 126 DisplayCutout cutout, Pair<Integer, Integer> cornerCutoutPadding, 127 int roundedCornerContentPadding) { 128 if (cutout == null) { 129 return new Pair<>(roundedCornerContentPadding, roundedCornerContentPadding); 130 } 131 132 // padding needed for corner cutout. 133 int leftCornerCutoutPadding = cutout.getSafeInsetLeft(); 134 int rightCornerCutoutPadding = cutout.getSafeInsetRight(); 135 if (cornerCutoutPadding != null) { 136 leftCornerCutoutPadding = Math.max(leftCornerCutoutPadding, cornerCutoutPadding.first); 137 rightCornerCutoutPadding = Math.max(rightCornerCutoutPadding, 138 cornerCutoutPadding.second); 139 } 140 141 return new Pair<>( 142 Math.max(leftCornerCutoutPadding, roundedCornerContentPadding), 143 Math.max(rightCornerCutoutPadding, roundedCornerContentPadding)); 144 } 145 146 147 /** 148 * Compute the corner cutout margins in portrait mode 149 */ cornerCutoutMargins(DisplayCutout cutout, Display display)150 public static Pair<Integer, Integer> cornerCutoutMargins(DisplayCutout cutout, 151 Display display) { 152 return statusBarCornerCutoutMargins(cutout, display, RotationUtils.ROTATION_NONE, 0); 153 } 154 155 /** 156 * Compute the corner cutout margins in the given orientation (exactRotation) 157 */ statusBarCornerCutoutMargins(DisplayCutout cutout, Display display, int exactRotation, int statusBarHeight)158 public static Pair<Integer, Integer> statusBarCornerCutoutMargins(DisplayCutout cutout, 159 Display display, int exactRotation, int statusBarHeight) { 160 if (cutout == null) { 161 return null; 162 } 163 Point size = new Point(); 164 display.getRealSize(size); 165 166 Rect bounds = new Rect(); 167 switch (exactRotation) { 168 case RotationUtils.ROTATION_LANDSCAPE: 169 boundsFromDirection(cutout, Gravity.LEFT, bounds); 170 break; 171 case RotationUtils.ROTATION_SEASCAPE: 172 boundsFromDirection(cutout, Gravity.RIGHT, bounds); 173 break; 174 case RotationUtils.ROTATION_NONE: 175 boundsFromDirection(cutout, Gravity.TOP, bounds); 176 break; 177 case RotationUtils.ROTATION_UPSIDE_DOWN: 178 // we assume the cutout is always on top in portrait mode 179 return null; 180 } 181 182 if (statusBarHeight >= 0 && bounds.top > statusBarHeight) { 183 return null; 184 } 185 186 if (bounds.left <= 0) { 187 return new Pair<>(bounds.right, 0); 188 } 189 190 if (bounds.right >= size.x) { 191 return new Pair<>(0, size.x - bounds.left); 192 } 193 194 return null; 195 } 196 } 197