• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
StatusBarWindowView(Context context, AttributeSet attrs)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
fitSystemWindows(Rect insets)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 
applyMargins()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
generateLayoutParams(AttributeSet attrs)117     public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
118         return new LayoutParams(getContext(), attrs);
119     }
120 
121     @Override
generateDefaultLayoutParams()122     protected FrameLayout.LayoutParams generateDefaultLayoutParams() {
123         return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
124     }
125 
126     @Override
onFinishInflate()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 
setService(PhoneStatusBar service)135     public void setService(PhoneStatusBar service) {
136         mService = service;
137         mDragDownHelper = new DragDownHelper(getContext(), this, mStackScrollLayout, mService);
138     }
139 
140     @Override
onAttachedToWindow()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
dispatchKeyEvent(KeyEvent event)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
dispatchTouchEvent(MotionEvent ev)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
onInterceptTouchEvent(MotionEvent ev)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
onTouchEvent(MotionEvent ev)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
onDraw(Canvas canvas)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 
cancelExpandHelper()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 
LayoutParams(int width, int height)295         public LayoutParams(int width, int height) {
296             super(width, height);
297         }
298 
LayoutParams(Context c, AttributeSet attrs)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