Home | History | Annotate | Download | only in phone
      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