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
17package com.android.systemui.statusbar.phone;
18
19import android.app.StatusBarManager;
20import android.content.Context;
21import android.content.res.TypedArray;
22import android.graphics.Canvas;
23import android.graphics.Paint;
24import android.graphics.PorterDuff;
25import android.graphics.PorterDuffXfermode;
26import android.graphics.Rect;
27import android.media.session.MediaSessionLegacyHelper;
28import android.os.IBinder;
29import android.util.AttributeSet;
30import android.view.KeyEvent;
31import android.view.MotionEvent;
32import android.view.View;
33import android.view.ViewRootImpl;
34import android.view.WindowManager;
35import android.view.WindowManagerGlobal;
36import android.widget.FrameLayout;
37
38import com.android.systemui.R;
39import com.android.systemui.statusbar.BaseStatusBar;
40import com.android.systemui.statusbar.DragDownHelper;
41import com.android.systemui.statusbar.StatusBarState;
42import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
43
44
45public 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