2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com
.android
.systemui
.statusbar
.phone
;
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
;
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
;
63 public class StatusBarWindowView
extends FrameLayout
{
64 public static final String TAG
= "StatusBarWindowView";
65 public static final boolean DEBUG
= BaseStatusBar
.DEBUG
;
67 private DragDownHelper mDragDownHelper
;
68 private NotificationStackScrollLayout mStackScrollLayout
;
69 private NotificationPanelView mNotificationPanel
;
70 private View mBrightnessMirror
;
72 private int mRightInset
= 0;
73 private int mLeftInset
= 0;
75 private PhoneStatusBar mService
;
76 private final Paint mTransparentSrcPaint
= new Paint();
77 private FalsingManager mFalsingManager
;
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
;
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
);
95 protected boolean fitSystemWindows(Rect insets
) {
96 if (getFitsSystemWindows()) {
97 boolean paddingChanged
= insets
.top
!= getPaddingTop()
98 || insets
.bottom
!= getPaddingBottom();
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
;
106 // Drop top inset, and pass through bottom inset.
107 if (paddingChanged
) {
108 setPadding(0, 0, 0, 0);
114 if (mRightInset
!= 0 || mLeftInset
!= 0) {
119 boolean changed
= getPaddingLeft() != 0
120 || getPaddingRight() != 0
121 || getPaddingTop() != 0
122 || getPaddingBottom() != 0;
124 setPadding(0, 0, 0, 0);
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();
149 public FrameLayout
.LayoutParams
generateLayoutParams(AttributeSet attrs
) {
150 return new LayoutParams(getContext(), attrs
);
154 protected FrameLayout
.LayoutParams
generateDefaultLayoutParams() {
155 return new LayoutParams(LayoutParams
.MATCH_PARENT
, LayoutParams
.MATCH_PARENT
);
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
);
167 public void setService(PhoneStatusBar service
) {
169 mDragDownHelper
= new DragDownHelper(getContext(), this, mStackScrollLayout
, mService
);
173 protected void onAttachedToWindow () {
174 super.onAttachedToWindow();
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
;
184 WindowManagerGlobal
.getInstance().changeCanvasOpacity(windowToken
, true);
185 setWillNotDraw(false);
187 setWillNotDraw(!DEBUG
);
192 public boolean dispatchKeyEvent(KeyEvent event
) {
193 if (mService
.interceptMediaKey(event
)) {
196 if (super.dispatchKeyEvent(event
)) {
199 boolean down
= event
.getAction() == KeyEvent
.ACTION_DOWN
;
200 switch (event
.getKeyCode()) {
201 case KeyEvent
.KEYCODE_BACK
:
203 mService
.onBackPressed();
206 case KeyEvent
.KEYCODE_MENU
:
208 return mService
.onMenuPressed();
210 case KeyEvent
.KEYCODE_SPACE
:
212 return mService
.onSpacePressed();
215 case KeyEvent
.KEYCODE_VOLUME_DOWN
:
216 case KeyEvent
.KEYCODE_VOLUME_UP
:
217 if (mService
.isDozing()) {
218 MediaSessionLegacyHelper
.getHelper(mContext
).sendVolumeKeyEvent(event
, true);
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
) {
237 if (ev
.getActionMasked() == MotionEvent
.ACTION_DOWN
) {
238 mStackScrollLayout
.closeControlsIfOutsideTouch(ev
);
241 return super.dispatchTouchEvent(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
);
258 super.onInterceptTouchEvent(ev
);
261 MotionEvent cancellation
= MotionEvent
.obtain(ev
);
262 cancellation
.setAction(MotionEvent
.ACTION_CANCEL
);
263 mStackScrollLayout
.onInterceptTouchEvent(cancellation
);
264 mNotificationPanel
.onInterceptTouchEvent(cancellation
);
265 cancellation
.recycle();
271 public boolean onTouchEvent(MotionEvent ev
) {
272 boolean handled
= false;
273 if (mService
.getBarState() == StatusBarState
.KEYGUARD
) {
274 handled
= mDragDownHelper
.onTouchEvent(ev
);
277 handled
= super.onTouchEvent(ev
);
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);
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
);
297 if (getPaddingBottom() != 0) {
298 canvas
.drawRect(0, paddedBottom
, getWidth(), getHeight(), mTransparentSrcPaint
);
300 if (getPaddingLeft() != 0) {
301 canvas
.drawRect(0, getPaddingTop(), getPaddingLeft(), paddedBottom
,
302 mTransparentSrcPaint
);
304 if (getPaddingRight() != 0) {
305 canvas
.drawRect(paddedRight
, getPaddingTop(), getWidth(), paddedBottom
,
306 mTransparentSrcPaint
);
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
);
318 public void cancelExpandHelper() {
319 if (mStackScrollLayout
!= null) {
320 mStackScrollLayout
.cancelExpandHelper();
324 public class LayoutParams
extends FrameLayout
.LayoutParams
{
326 public boolean ignoreRightInset
;
328 public LayoutParams(int width
, int height
) {
329 super(width
, height
);
332 public LayoutParams(Context c
, AttributeSet attrs
) {
335 TypedArray a
= c
.obtainStyledAttributes(attrs
, R
.styleable
.StatusBarWindowView_Layout
);
336 ignoreRightInset
= a
.getBoolean(
337 R
.styleable
.StatusBarWindowView_Layout_ignoreRightInset
, false);
343 public ActionMode
startActionModeForChild(View originalView
, ActionMode
.Callback callback
,
345 if (type
== ActionMode
.TYPE_FLOATING
) {
346 return startActionMode(originalView
, callback
, type
);
348 return super.startActionModeForChild(originalView
, callback
, type
);
351 private ActionMode
createFloatingActionMode(
352 View originatingView
, ActionMode
.Callback2 callback
) {
353 if (mFloatingActionMode
!= null) {
354 mFloatingActionMode
.finish();
356 cleanupFloatingActionModeViews();
357 final FloatingActionMode mode
=
358 new FloatingActionMode(mContext
, callback
, originatingView
);
359 mFloatingActionModeOriginatingView
= originatingView
;
360 mFloatingToolbarPreDrawListener
=
361 new ViewTreeObserver
.OnPreDrawListener() {
363 public boolean onPreDraw() {
364 mode
.updateViewLocationInWindow();
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
);
380 private void cleanupFloatingActionModeViews() {
381 if (mFloatingToolbar
!= null) {
382 mFloatingToolbar
.dismiss();
383 mFloatingToolbar
= null;
385 if (mFloatingActionModeOriginatingView
!= null) {
386 if (mFloatingToolbarPreDrawListener
!= null) {
387 mFloatingActionModeOriginatingView
.getViewTreeObserver()
388 .removeOnPreDrawListener(mFloatingToolbarPreDrawListener
);
389 mFloatingToolbarPreDrawListener
= null;
391 mFloatingActionModeOriginatingView
= null;
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
);
407 private class ActionModeCallback2Wrapper
extends ActionMode
.Callback2
{
408 private final ActionMode
.Callback mWrapped
;
410 public ActionModeCallback2Wrapper(ActionMode
.Callback wrapped
) {
414 public boolean onCreateActionMode(ActionMode mode
, Menu menu
) {
415 return mWrapped
.onCreateActionMode(mode
, menu
);
418 public boolean onPrepareActionMode(ActionMode mode
, Menu menu
) {
419 requestFitSystemWindows();
420 return mWrapped
.onPrepareActionMode(mode
, menu
);
423 public boolean onActionItemClicked(ActionMode mode
, MenuItem item
) {
424 return mWrapped
.onActionItemClicked(mode
, item
);
427 public void onDestroyActionMode(ActionMode mode
) {
428 mWrapped
.onDestroyActionMode(mode
);
429 if (mode
== mFloatingActionMode
) {
430 cleanupFloatingActionModeViews();
431 mFloatingActionMode
= null;
433 requestFitSystemWindows();
437 public void onGetContentRect(ActionMode mode
, View view
, Rect outRect
) {
438 if (mWrapped
instanceof ActionMode
.Callback2
) {
439 ((ActionMode
.Callback2
) mWrapped
).onGetContentRect(mode
, view
, outRect
);
441 super.onGetContentRect(mode
, view
, outRect
);
447 * Minimal window to satisfy FloatingToolbar.
449 private Window mFakeWindow
= new Window(mContext
) {
451 public void takeSurface(SurfaceHolder
.Callback2 callback
) {
455 public void takeInputQueue(InputQueue
.Callback callback
) {
459 public boolean isFloating() {
464 public void alwaysReadCloseOnTouchAttr() {
468 public void setContentView(@LayoutRes int layoutResID
) {
472 public void setContentView(View view
) {
476 public void setContentView(View view
, ViewGroup
.LayoutParams params
) {
480 public void addContentView(View view
, ViewGroup
.LayoutParams params
) {
484 public void clearContentView() {
488 public View
getCurrentFocus() {
493 public LayoutInflater
getLayoutInflater() {
498 public void setTitle(CharSequence title
) {
502 public void setTitleColor(@ColorInt int textColor
) {
506 public void openPanel(int featureId
, KeyEvent event
) {
510 public void closePanel(int featureId
) {
514 public void togglePanel(int featureId
, KeyEvent event
) {
518 public void invalidatePanelMenu(int featureId
) {
522 public boolean performPanelShortcut(int featureId
, int keyCode
, KeyEvent event
, int flags
) {
527 public boolean performPanelIdentifierAction(int featureId
, int id
, int flags
) {
532 public void closeAllPanels() {
536 public boolean performContextMenuIdentifierAction(int id
, int flags
) {
541 public void onConfigurationChanged(Configuration newConfig
) {
545 public void setBackgroundDrawable(Drawable drawable
) {
549 public void setFeatureDrawableResource(int featureId
, @DrawableRes int resId
) {
553 public void setFeatureDrawableUri(int featureId
, Uri uri
) {
557 public void setFeatureDrawable(int featureId
, Drawable drawable
) {
561 public void setFeatureDrawableAlpha(int featureId
, int alpha
) {
565 public void setFeatureInt(int featureId
, int value
) {
569 public void takeKeyEvents(boolean get
) {
573 public boolean superDispatchKeyEvent(KeyEvent event
) {
578 public boolean superDispatchKeyShortcutEvent(KeyEvent event
) {
583 public boolean superDispatchTouchEvent(MotionEvent event
) {
588 public boolean superDispatchTrackballEvent(MotionEvent event
) {
593 public boolean superDispatchGenericMotionEvent(MotionEvent event
) {
598 public View
getDecorView() {
599 return StatusBarWindowView
.this;
603 public View
peekDecorView() {
608 public Bundle
saveHierarchyState() {
613 public void restoreHierarchyState(Bundle savedInstanceState
) {
617 protected void onActive() {
621 public void setChildDrawable(int featureId
, Drawable drawable
) {
625 public void setChildInt(int featureId
, int value
) {
629 public boolean isShortcutKey(int keyCode
, KeyEvent event
) {
634 public void setVolumeControlStream(int streamType
) {
638 public int getVolumeControlStream() {
643 public int getStatusBarColor() {
648 public void setStatusBarColor(@ColorInt int color
) {
652 public int getNavigationBarColor() {
657 public void setNavigationBarColor(@ColorInt int color
) {
661 public void setDecorCaptionShade(int decorCaptionShade
) {
665 public void setResizingCaptionDrawable(Drawable drawable
) {
669 public void onMultiWindowModeChanged() {
673 public void reportActivityRelaunched() {