• 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.annotation.ColorInt;
20 import android.annotation.DrawableRes;
21 import android.annotation.LayoutRes;
22 import android.app.StatusBarManager;
23 import android.content.Context;
24 import android.content.res.Configuration;
25 import android.content.res.TypedArray;
26 import android.graphics.Canvas;
27 import android.graphics.Paint;
28 import android.graphics.PorterDuff;
29 import android.graphics.PorterDuffXfermode;
30 import android.graphics.Rect;
31 import android.graphics.drawable.Drawable;
32 import android.media.session.MediaSessionLegacyHelper;
33 import android.net.Uri;
34 import android.os.Bundle;
35 import android.os.IBinder;
36 import android.util.AttributeSet;
37 import android.view.ActionMode;
38 import android.view.InputQueue;
39 import android.view.KeyEvent;
40 import android.view.LayoutInflater;
41 import android.view.Menu;
42 import android.view.MenuItem;
43 import android.view.MotionEvent;
44 import android.view.SurfaceHolder;
45 import android.view.View;
46 import android.view.ViewGroup;
47 import android.view.ViewTreeObserver;
48 import android.view.Window;
49 import android.view.WindowManager;
50 import android.view.WindowManagerGlobal;
51 import android.widget.FrameLayout;
52 
53 import com.android.internal.view.FloatingActionMode;
54 import com.android.internal.widget.FloatingToolbar;
55 import com.android.systemui.R;
56 import com.android.systemui.classifier.FalsingManager;
57 import com.android.systemui.statusbar.BaseStatusBar;
58 import com.android.systemui.statusbar.DragDownHelper;
59 import com.android.systemui.statusbar.StatusBarState;
60 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
61 
62 
63 public class StatusBarWindowView extends FrameLayout {
64     public static final String TAG = "StatusBarWindowView";
65     public static final boolean DEBUG = BaseStatusBar.DEBUG;
66 
67     private DragDownHelper mDragDownHelper;
68     private NotificationStackScrollLayout mStackScrollLayout;
69     private NotificationPanelView mNotificationPanel;
70     private View mBrightnessMirror;
71 
72     private int mRightInset = 0;
73     private int mLeftInset = 0;
74 
75     private PhoneStatusBar mService;
76     private final Paint mTransparentSrcPaint = new Paint();
77     private FalsingManager mFalsingManager;
78 
79     // Implements the floating action mode for TextView's Cut/Copy/Past menu. Normally provided by
80     // DecorView, but since this is a special window we have to roll our own.
81     private View mFloatingActionModeOriginatingView;
82     private ActionMode mFloatingActionMode;
83     private FloatingToolbar mFloatingToolbar;
84     private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
85 
StatusBarWindowView(Context context, AttributeSet attrs)86     public StatusBarWindowView(Context context, AttributeSet attrs) {
87         super(context, attrs);
88         setMotionEventSplittingEnabled(false);
89         mTransparentSrcPaint.setColor(0);
90         mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
91         mFalsingManager = FalsingManager.getInstance(context);
92     }
93 
94     @Override
fitSystemWindows(Rect insets)95     protected boolean fitSystemWindows(Rect insets) {
96         if (getFitsSystemWindows()) {
97             boolean paddingChanged = insets.top != getPaddingTop()
98                     || insets.bottom != getPaddingBottom();
99 
100             // Super-special right inset handling, because scrims and backdrop need to ignore it.
101             if (insets.right != mRightInset || insets.left != mLeftInset) {
102                 mRightInset = insets.right;
103                 mLeftInset = insets.left;
104                 applyMargins();
105             }
106             // Drop top inset, and pass through bottom inset.
107             if (paddingChanged) {
108                 setPadding(0, 0, 0, 0);
109             }
110             insets.left = 0;
111             insets.top = 0;
112             insets.right = 0;
113         } else {
114             if (mRightInset != 0 || mLeftInset != 0) {
115                 mRightInset = 0;
116                 mLeftInset = 0;
117                 applyMargins();
118             }
119             boolean changed = getPaddingLeft() != 0
120                     || getPaddingRight() != 0
121                     || getPaddingTop() != 0
122                     || getPaddingBottom() != 0;
123             if (changed) {
124                 setPadding(0, 0, 0, 0);
125             }
126             insets.top = 0;
127         }
128         return false;
129     }
130 
applyMargins()131     private void applyMargins() {
132         mService.mScrimController.setLeftInset(mLeftInset);
133         final int N = getChildCount();
134         for (int i = 0; i < N; i++) {
135             View child = getChildAt(i);
136             if (child.getLayoutParams() instanceof LayoutParams) {
137                 LayoutParams lp = (LayoutParams) child.getLayoutParams();
138                 if (!lp.ignoreRightInset
139                         && (lp.rightMargin != mRightInset || lp.leftMargin != mLeftInset)) {
140                     lp.rightMargin = mRightInset;
141                     lp.leftMargin = mLeftInset;
142                     child.requestLayout();
143                 }
144             }
145         }
146     }
147 
148     @Override
generateLayoutParams(AttributeSet attrs)149     public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
150         return new LayoutParams(getContext(), attrs);
151     }
152 
153     @Override
generateDefaultLayoutParams()154     protected FrameLayout.LayoutParams generateDefaultLayoutParams() {
155         return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
156     }
157 
158     @Override
onFinishInflate()159     protected void onFinishInflate() {
160         super.onFinishInflate();
161         mStackScrollLayout = (NotificationStackScrollLayout) findViewById(
162                 R.id.notification_stack_scroller);
163         mNotificationPanel = (NotificationPanelView) findViewById(R.id.notification_panel);
164         mBrightnessMirror = findViewById(R.id.brightness_mirror);
165     }
166 
setService(PhoneStatusBar service)167     public void setService(PhoneStatusBar service) {
168         mService = service;
169         mDragDownHelper = new DragDownHelper(getContext(), this, mStackScrollLayout, mService);
170     }
171 
172     @Override
onAttachedToWindow()173     protected void onAttachedToWindow () {
174         super.onAttachedToWindow();
175 
176         // We need to ensure that our window doesn't suffer from overdraw which would normally
177         // occur if our window is translucent. Since we are drawing the whole window anyway with
178         // the scrim, we don't need the window to be cleared in the beginning.
179         if (mService.isScrimSrcModeEnabled()) {
180             IBinder windowToken = getWindowToken();
181             WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
182             lp.token = windowToken;
183             setLayoutParams(lp);
184             WindowManagerGlobal.getInstance().changeCanvasOpacity(windowToken, true);
185             setWillNotDraw(false);
186         } else {
187             setWillNotDraw(!DEBUG);
188         }
189     }
190 
191     @Override
dispatchKeyEvent(KeyEvent event)192     public boolean dispatchKeyEvent(KeyEvent event) {
193         if (mService.interceptMediaKey(event)) {
194             return true;
195         }
196         if (super.dispatchKeyEvent(event)) {
197             return true;
198         }
199         boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
200         switch (event.getKeyCode()) {
201             case KeyEvent.KEYCODE_BACK:
202                 if (!down) {
203                     mService.onBackPressed();
204                 }
205                 return true;
206             case KeyEvent.KEYCODE_MENU:
207                 if (!down) {
208                     return mService.onMenuPressed();
209                 }
210             case KeyEvent.KEYCODE_SPACE:
211                 if (!down) {
212                     return mService.onSpacePressed();
213                 }
214                 break;
215             case KeyEvent.KEYCODE_VOLUME_DOWN:
216             case KeyEvent.KEYCODE_VOLUME_UP:
217                 if (mService.isDozing()) {
218                     MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(event, true);
219                     return true;
220                 }
221                 break;
222         }
223         return false;
224     }
225 
226     @Override
dispatchTouchEvent(MotionEvent ev)227     public boolean dispatchTouchEvent(MotionEvent ev) {
228         mFalsingManager.onTouchEvent(ev, getWidth(), getHeight());
229         if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == VISIBLE) {
230             // Disallow new pointers while the brightness mirror is visible. This is so that you
231             // can't touch anything other than the brightness slider while the mirror is showing
232             // and the rest of the panel is transparent.
233             if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
234                 return false;
235             }
236         }
237         if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
238             mStackScrollLayout.closeControlsIfOutsideTouch(ev);
239         }
240 
241         return super.dispatchTouchEvent(ev);
242     }
243 
244     @Override
onInterceptTouchEvent(MotionEvent ev)245     public boolean onInterceptTouchEvent(MotionEvent ev) {
246         boolean intercept = false;
247         if (mNotificationPanel.isFullyExpanded()
248                 && mStackScrollLayout.getVisibility() == View.VISIBLE
249                 && mService.getBarState() == StatusBarState.KEYGUARD
250                 && !mService.isBouncerShowing()) {
251             intercept = mDragDownHelper.onInterceptTouchEvent(ev);
252             // wake up on a touch down event, if dozing
253             if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
254                 mService.wakeUpIfDozing(ev.getEventTime(), ev);
255             }
256         }
257         if (!intercept) {
258             super.onInterceptTouchEvent(ev);
259         }
260         if (intercept) {
261             MotionEvent cancellation = MotionEvent.obtain(ev);
262             cancellation.setAction(MotionEvent.ACTION_CANCEL);
263             mStackScrollLayout.onInterceptTouchEvent(cancellation);
264             mNotificationPanel.onInterceptTouchEvent(cancellation);
265             cancellation.recycle();
266         }
267         return intercept;
268     }
269 
270     @Override
onTouchEvent(MotionEvent ev)271     public boolean onTouchEvent(MotionEvent ev) {
272         boolean handled = false;
273         if (mService.getBarState() == StatusBarState.KEYGUARD) {
274             handled = mDragDownHelper.onTouchEvent(ev);
275         }
276         if (!handled) {
277             handled = super.onTouchEvent(ev);
278         }
279         final int action = ev.getAction();
280         if (!handled && (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL)) {
281             mService.setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
282         }
283         return handled;
284     }
285 
286     @Override
onDraw(Canvas canvas)287     public void onDraw(Canvas canvas) {
288         super.onDraw(canvas);
289         if (mService.isScrimSrcModeEnabled()) {
290             // We need to ensure that our window is always drawn fully even when we have paddings,
291             // since we simulate it to be opaque.
292             int paddedBottom = getHeight() - getPaddingBottom();
293             int paddedRight = getWidth() - getPaddingRight();
294             if (getPaddingTop() != 0) {
295                 canvas.drawRect(0, 0, getWidth(), getPaddingTop(), mTransparentSrcPaint);
296             }
297             if (getPaddingBottom() != 0) {
298                 canvas.drawRect(0, paddedBottom, getWidth(), getHeight(), mTransparentSrcPaint);
299             }
300             if (getPaddingLeft() != 0) {
301                 canvas.drawRect(0, getPaddingTop(), getPaddingLeft(), paddedBottom,
302                         mTransparentSrcPaint);
303             }
304             if (getPaddingRight() != 0) {
305                 canvas.drawRect(paddedRight, getPaddingTop(), getWidth(), paddedBottom,
306                         mTransparentSrcPaint);
307             }
308         }
309         if (DEBUG) {
310             Paint pt = new Paint();
311             pt.setColor(0x80FFFF00);
312             pt.setStrokeWidth(12.0f);
313             pt.setStyle(Paint.Style.STROKE);
314             canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), pt);
315         }
316     }
317 
cancelExpandHelper()318     public void cancelExpandHelper() {
319         if (mStackScrollLayout != null) {
320             mStackScrollLayout.cancelExpandHelper();
321         }
322     }
323 
324     public class LayoutParams extends FrameLayout.LayoutParams {
325 
326         public boolean ignoreRightInset;
327 
LayoutParams(int width, int height)328         public LayoutParams(int width, int height) {
329             super(width, height);
330         }
331 
LayoutParams(Context c, AttributeSet attrs)332         public LayoutParams(Context c, AttributeSet attrs) {
333             super(c, attrs);
334 
335             TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.StatusBarWindowView_Layout);
336             ignoreRightInset = a.getBoolean(
337                     R.styleable.StatusBarWindowView_Layout_ignoreRightInset, false);
338             a.recycle();
339         }
340     }
341 
342     @Override
startActionModeForChild(View originalView, ActionMode.Callback callback, int type)343     public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback,
344             int type) {
345         if (type == ActionMode.TYPE_FLOATING) {
346             return startActionMode(originalView, callback, type);
347         }
348         return super.startActionModeForChild(originalView, callback, type);
349     }
350 
createFloatingActionMode( View originatingView, ActionMode.Callback2 callback)351     private ActionMode createFloatingActionMode(
352             View originatingView, ActionMode.Callback2 callback) {
353         if (mFloatingActionMode != null) {
354             mFloatingActionMode.finish();
355         }
356         cleanupFloatingActionModeViews();
357         final FloatingActionMode mode =
358                 new FloatingActionMode(mContext, callback, originatingView);
359         mFloatingActionModeOriginatingView = originatingView;
360         mFloatingToolbarPreDrawListener =
361                 new ViewTreeObserver.OnPreDrawListener() {
362                     @Override
363                     public boolean onPreDraw() {
364                         mode.updateViewLocationInWindow();
365                         return true;
366                     }
367                 };
368         return mode;
369     }
370 
setHandledFloatingActionMode(ActionMode mode)371     private void setHandledFloatingActionMode(ActionMode mode) {
372         mFloatingActionMode = mode;
373         mFloatingToolbar = new FloatingToolbar(mContext, mFakeWindow);
374         ((FloatingActionMode) mFloatingActionMode).setFloatingToolbar(mFloatingToolbar);
375         mFloatingActionMode.invalidate();  // Will show the floating toolbar if necessary.
376         mFloatingActionModeOriginatingView.getViewTreeObserver()
377                 .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
378     }
379 
cleanupFloatingActionModeViews()380     private void cleanupFloatingActionModeViews() {
381         if (mFloatingToolbar != null) {
382             mFloatingToolbar.dismiss();
383             mFloatingToolbar = null;
384         }
385         if (mFloatingActionModeOriginatingView != null) {
386             if (mFloatingToolbarPreDrawListener != null) {
387                 mFloatingActionModeOriginatingView.getViewTreeObserver()
388                         .removeOnPreDrawListener(mFloatingToolbarPreDrawListener);
389                 mFloatingToolbarPreDrawListener = null;
390             }
391             mFloatingActionModeOriginatingView = null;
392         }
393     }
394 
startActionMode( View originatingView, ActionMode.Callback callback, int type)395     private ActionMode startActionMode(
396             View originatingView, ActionMode.Callback callback, int type) {
397         ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
398         ActionMode mode = createFloatingActionMode(originatingView, wrappedCallback);
399         if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
400             setHandledFloatingActionMode(mode);
401         } else {
402             mode = null;
403         }
404         return mode;
405     }
406 
407     private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
408         private final ActionMode.Callback mWrapped;
409 
ActionModeCallback2Wrapper(ActionMode.Callback wrapped)410         public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
411             mWrapped = wrapped;
412         }
413 
onCreateActionMode(ActionMode mode, Menu menu)414         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
415             return mWrapped.onCreateActionMode(mode, menu);
416         }
417 
onPrepareActionMode(ActionMode mode, Menu menu)418         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
419             requestFitSystemWindows();
420             return mWrapped.onPrepareActionMode(mode, menu);
421         }
422 
onActionItemClicked(ActionMode mode, MenuItem item)423         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
424             return mWrapped.onActionItemClicked(mode, item);
425         }
426 
onDestroyActionMode(ActionMode mode)427         public void onDestroyActionMode(ActionMode mode) {
428             mWrapped.onDestroyActionMode(mode);
429             if (mode == mFloatingActionMode) {
430                 cleanupFloatingActionModeViews();
431                 mFloatingActionMode = null;
432             }
433             requestFitSystemWindows();
434         }
435 
436         @Override
onGetContentRect(ActionMode mode, View view, Rect outRect)437         public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
438             if (mWrapped instanceof ActionMode.Callback2) {
439                 ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
440             } else {
441                 super.onGetContentRect(mode, view, outRect);
442             }
443         }
444     }
445 
446     /**
447      * Minimal window to satisfy FloatingToolbar.
448      */
449     private Window mFakeWindow = new Window(mContext) {
450         @Override
451         public void takeSurface(SurfaceHolder.Callback2 callback) {
452         }
453 
454         @Override
455         public void takeInputQueue(InputQueue.Callback callback) {
456         }
457 
458         @Override
459         public boolean isFloating() {
460             return false;
461         }
462 
463         @Override
464         public void alwaysReadCloseOnTouchAttr() {
465         }
466 
467         @Override
468         public void setContentView(@LayoutRes int layoutResID) {
469         }
470 
471         @Override
472         public void setContentView(View view) {
473         }
474 
475         @Override
476         public void setContentView(View view, ViewGroup.LayoutParams params) {
477         }
478 
479         @Override
480         public void addContentView(View view, ViewGroup.LayoutParams params) {
481         }
482 
483         @Override
484         public void clearContentView() {
485         }
486 
487         @Override
488         public View getCurrentFocus() {
489             return null;
490         }
491 
492         @Override
493         public LayoutInflater getLayoutInflater() {
494             return null;
495         }
496 
497         @Override
498         public void setTitle(CharSequence title) {
499         }
500 
501         @Override
502         public void setTitleColor(@ColorInt int textColor) {
503         }
504 
505         @Override
506         public void openPanel(int featureId, KeyEvent event) {
507         }
508 
509         @Override
510         public void closePanel(int featureId) {
511         }
512 
513         @Override
514         public void togglePanel(int featureId, KeyEvent event) {
515         }
516 
517         @Override
518         public void invalidatePanelMenu(int featureId) {
519         }
520 
521         @Override
522         public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) {
523             return false;
524         }
525 
526         @Override
527         public boolean performPanelIdentifierAction(int featureId, int id, int flags) {
528             return false;
529         }
530 
531         @Override
532         public void closeAllPanels() {
533         }
534 
535         @Override
536         public boolean performContextMenuIdentifierAction(int id, int flags) {
537             return false;
538         }
539 
540         @Override
541         public void onConfigurationChanged(Configuration newConfig) {
542         }
543 
544         @Override
545         public void setBackgroundDrawable(Drawable drawable) {
546         }
547 
548         @Override
549         public void setFeatureDrawableResource(int featureId, @DrawableRes int resId) {
550         }
551 
552         @Override
553         public void setFeatureDrawableUri(int featureId, Uri uri) {
554         }
555 
556         @Override
557         public void setFeatureDrawable(int featureId, Drawable drawable) {
558         }
559 
560         @Override
561         public void setFeatureDrawableAlpha(int featureId, int alpha) {
562         }
563 
564         @Override
565         public void setFeatureInt(int featureId, int value) {
566         }
567 
568         @Override
569         public void takeKeyEvents(boolean get) {
570         }
571 
572         @Override
573         public boolean superDispatchKeyEvent(KeyEvent event) {
574             return false;
575         }
576 
577         @Override
578         public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
579             return false;
580         }
581 
582         @Override
583         public boolean superDispatchTouchEvent(MotionEvent event) {
584             return false;
585         }
586 
587         @Override
588         public boolean superDispatchTrackballEvent(MotionEvent event) {
589             return false;
590         }
591 
592         @Override
593         public boolean superDispatchGenericMotionEvent(MotionEvent event) {
594             return false;
595         }
596 
597         @Override
598         public View getDecorView() {
599             return StatusBarWindowView.this;
600         }
601 
602         @Override
603         public View peekDecorView() {
604             return null;
605         }
606 
607         @Override
608         public Bundle saveHierarchyState() {
609             return null;
610         }
611 
612         @Override
613         public void restoreHierarchyState(Bundle savedInstanceState) {
614         }
615 
616         @Override
617         protected void onActive() {
618         }
619 
620         @Override
621         public void setChildDrawable(int featureId, Drawable drawable) {
622         }
623 
624         @Override
625         public void setChildInt(int featureId, int value) {
626         }
627 
628         @Override
629         public boolean isShortcutKey(int keyCode, KeyEvent event) {
630             return false;
631         }
632 
633         @Override
634         public void setVolumeControlStream(int streamType) {
635         }
636 
637         @Override
638         public int getVolumeControlStream() {
639             return 0;
640         }
641 
642         @Override
643         public int getStatusBarColor() {
644             return 0;
645         }
646 
647         @Override
648         public void setStatusBarColor(@ColorInt int color) {
649         }
650 
651         @Override
652         public int getNavigationBarColor() {
653             return 0;
654         }
655 
656         @Override
657         public void setNavigationBarColor(@ColorInt int color) {
658         }
659 
660         @Override
661         public void setDecorCaptionShade(int decorCaptionShade) {
662         }
663 
664         @Override
665         public void setResizingCaptionDrawable(Drawable drawable) {
666         }
667 
668         @Override
669         public void onMultiWindowModeChanged() {
670         }
671 
672         @Override
673         public void reportActivityRelaunched() {
674         }
675     };
676 
677 }
678 
679