1 /* 2 * Copyright (C) 2012 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.StatusBarManager; 20 import android.content.Context; 21 import android.content.res.TypedArray; 22 import android.graphics.Canvas; 23 import android.graphics.Paint; 24 import android.graphics.PorterDuff; 25 import android.graphics.PorterDuffXfermode; 26 import android.graphics.Rect; 27 import android.media.session.MediaSessionLegacyHelper; 28 import android.os.IBinder; 29 import android.util.AttributeSet; 30 import android.view.KeyEvent; 31 import android.view.MotionEvent; 32 import android.view.View; 33 import android.view.ViewRootImpl; 34 import android.view.WindowManager; 35 import android.view.WindowManagerGlobal; 36 import android.widget.FrameLayout; 37 38 import com.android.systemui.R; 39 import com.android.systemui.statusbar.BaseStatusBar; 40 import com.android.systemui.statusbar.DragDownHelper; 41 import com.android.systemui.statusbar.StatusBarState; 42 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; 43 44 45 public class StatusBarWindowView extends FrameLayout { 46 public static final String TAG = "StatusBarWindowView"; 47 public static final boolean DEBUG = BaseStatusBar.DEBUG; 48 49 private DragDownHelper mDragDownHelper; 50 private NotificationStackScrollLayout mStackScrollLayout; 51 private NotificationPanelView mNotificationPanel; 52 private View mBrightnessMirror; 53 54 private int mRightInset = 0; 55 56 private PhoneStatusBar mService; 57 private final Paint mTransparentSrcPaint = new Paint(); 58 59 public StatusBarWindowView(Context context, AttributeSet attrs) { 60 super(context, attrs); 61 setMotionEventSplittingEnabled(false); 62 mTransparentSrcPaint.setColor(0); 63 mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); 64 } 65 66 @Override 67 protected boolean fitSystemWindows(Rect insets) { 68 if (getFitsSystemWindows()) { 69 boolean paddingChanged = insets.left != getPaddingLeft() 70 || insets.top != getPaddingTop() 71 || insets.bottom != getPaddingBottom(); 72 73 // Super-special right inset handling, because scrims and backdrop need to ignore it. 74 if (insets.right != mRightInset) { 75 mRightInset = insets.right; 76 applyMargins(); 77 } 78 // Drop top inset, apply left inset and pass through bottom inset. 79 if (paddingChanged) { 80 setPadding(insets.left, 0, 0, 0); 81 } 82 insets.left = 0; 83 insets.top = 0; 84 insets.right = 0; 85 } else { 86 if (mRightInset != 0) { 87 mRightInset = 0; 88 applyMargins(); 89 } 90 boolean changed = getPaddingLeft() != 0 91 || getPaddingRight() != 0 92 || getPaddingTop() != 0 93 || getPaddingBottom() != 0; 94 if (changed) { 95 setPadding(0, 0, 0, 0); 96 } 97 insets.top = 0; 98 } 99 return false; 100 } 101 102 private void applyMargins() { 103 final int N = getChildCount(); 104 for (int i = 0; i < N; i++) { 105 View child = getChildAt(i); 106 if (child.getLayoutParams() instanceof LayoutParams) { 107 LayoutParams lp = (LayoutParams) child.getLayoutParams(); 108 if (!lp.ignoreRightInset && lp.rightMargin != mRightInset) { 109 lp.rightMargin = mRightInset; 110 child.requestLayout(); 111 } 112 } 113 } 114 } 115 116 @Override 117 public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) { 118 return new LayoutParams(getContext(), attrs); 119 } 120 121 @Override 122 protected FrameLayout.LayoutParams generateDefaultLayoutParams() { 123 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 124 } 125 126 @Override 127 protected void onFinishInflate() { 128 super.onFinishInflate(); 129 mStackScrollLayout = (NotificationStackScrollLayout) findViewById( 130 R.id.notification_stack_scroller); 131 mNotificationPanel = (NotificationPanelView) findViewById(R.id.notification_panel); 132 mBrightnessMirror = findViewById(R.id.brightness_mirror); 133 } 134 135 public void setService(PhoneStatusBar service) { 136 mService = service; 137 mDragDownHelper = new DragDownHelper(getContext(), this, mStackScrollLayout, mService); 138 } 139 140 @Override 141 protected void onAttachedToWindow () { 142 super.onAttachedToWindow(); 143 144 // We really need to be able to animate while window animations are going on 145 // so that activities may be started asynchronously from panel animations 146 final ViewRootImpl root = getViewRootImpl(); 147 if (root != null) { 148 root.setDrawDuringWindowsAnimating(true); 149 } 150 151 // We need to ensure that our window doesn't suffer from overdraw which would normally 152 // occur if our window is translucent. Since we are drawing the whole window anyway with 153 // the scrim, we don't need the window to be cleared in the beginning. 154 if (mService.isScrimSrcModeEnabled()) { 155 IBinder windowToken = getWindowToken(); 156 WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams(); 157 lp.token = windowToken; 158 setLayoutParams(lp); 159 WindowManagerGlobal.getInstance().changeCanvasOpacity(windowToken, true); 160 setWillNotDraw(false); 161 } else { 162 setWillNotDraw(!DEBUG); 163 } 164 } 165 166 @Override 167 public boolean dispatchKeyEvent(KeyEvent event) { 168 boolean down = event.getAction() == KeyEvent.ACTION_DOWN; 169 switch (event.getKeyCode()) { 170 case KeyEvent.KEYCODE_BACK: 171 if (!down) { 172 mService.onBackPressed(); 173 } 174 return true; 175 case KeyEvent.KEYCODE_MENU: 176 if (!down) { 177 return mService.onMenuPressed(); 178 } 179 case KeyEvent.KEYCODE_SPACE: 180 if (!down) { 181 return mService.onSpacePressed(); 182 } 183 break; 184 case KeyEvent.KEYCODE_VOLUME_DOWN: 185 case KeyEvent.KEYCODE_VOLUME_UP: 186 if (mService.isDozing()) { 187 MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, true); 188 return true; 189 } 190 break; 191 } 192 if (mService.interceptMediaKey(event)) { 193 return true; 194 } 195 return super.dispatchKeyEvent(event); 196 } 197 198 @Override 199 public boolean dispatchTouchEvent(MotionEvent ev) { 200 if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == VISIBLE) { 201 // Disallow new pointers while the brightness mirror is visible. This is so that you 202 // can't touch anything other than the brightness slider while the mirror is showing 203 // and the rest of the panel is transparent. 204 if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { 205 return false; 206 } 207 } 208 return super.dispatchTouchEvent(ev); 209 } 210 211 @Override 212 public boolean onInterceptTouchEvent(MotionEvent ev) { 213 boolean intercept = false; 214 if (mNotificationPanel.isFullyExpanded() 215 && mStackScrollLayout.getVisibility() == View.VISIBLE 216 && mService.getBarState() == StatusBarState.KEYGUARD 217 && !mService.isBouncerShowing()) { 218 intercept = mDragDownHelper.onInterceptTouchEvent(ev); 219 // wake up on a touch down event, if dozing 220 if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { 221 mService.wakeUpIfDozing(ev.getEventTime(), ev); 222 } 223 } 224 if (!intercept) { 225 super.onInterceptTouchEvent(ev); 226 } 227 if (intercept) { 228 MotionEvent cancellation = MotionEvent.obtain(ev); 229 cancellation.setAction(MotionEvent.ACTION_CANCEL); 230 mStackScrollLayout.onInterceptTouchEvent(cancellation); 231 mNotificationPanel.onInterceptTouchEvent(cancellation); 232 cancellation.recycle(); 233 } 234 return intercept; 235 } 236 237 @Override 238 public boolean onTouchEvent(MotionEvent ev) { 239 boolean handled = false; 240 if (mService.getBarState() == StatusBarState.KEYGUARD) { 241 handled = mDragDownHelper.onTouchEvent(ev); 242 } 243 if (!handled) { 244 handled = super.onTouchEvent(ev); 245 } 246 final int action = ev.getAction(); 247 if (!handled && (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL)) { 248 mService.setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); 249 } 250 return handled; 251 } 252 253 @Override 254 public void onDraw(Canvas canvas) { 255 super.onDraw(canvas); 256 if (mService.isScrimSrcModeEnabled()) { 257 // We need to ensure that our window is always drawn fully even when we have paddings, 258 // since we simulate it to be opaque. 259 int paddedBottom = getHeight() - getPaddingBottom(); 260 int paddedRight = getWidth() - getPaddingRight(); 261 if (getPaddingTop() != 0) { 262 canvas.drawRect(0, 0, getWidth(), getPaddingTop(), mTransparentSrcPaint); 263 } 264 if (getPaddingBottom() != 0) { 265 canvas.drawRect(0, paddedBottom, getWidth(), getHeight(), mTransparentSrcPaint); 266 } 267 if (getPaddingLeft() != 0) { 268 canvas.drawRect(0, getPaddingTop(), getPaddingLeft(), paddedBottom, 269 mTransparentSrcPaint); 270 } 271 if (getPaddingRight() != 0) { 272 canvas.drawRect(paddedRight, getPaddingTop(), getWidth(), paddedBottom, 273 mTransparentSrcPaint); 274 } 275 } 276 if (DEBUG) { 277 Paint pt = new Paint(); 278 pt.setColor(0x80FFFF00); 279 pt.setStrokeWidth(12.0f); 280 pt.setStyle(Paint.Style.STROKE); 281 canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), pt); 282 } 283 } 284 285 public void cancelExpandHelper() { 286 if (mStackScrollLayout != null) { 287 mStackScrollLayout.cancelExpandHelper(); 288 } 289 } 290 291 public class LayoutParams extends FrameLayout.LayoutParams { 292 293 public boolean ignoreRightInset; 294 295 public LayoutParams(int width, int height) { 296 super(width, height); 297 } 298 299 public LayoutParams(Context c, AttributeSet attrs) { 300 super(c, attrs); 301 302 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.StatusBarWindowView_Layout); 303 ignoreRightInset = a.getBoolean( 304 R.styleable.StatusBarWindowView_Layout_ignoreRightInset, false); 305 a.recycle(); 306 } 307 } 308 } 309 310