PhoneStatusBarView.java revision cfc359a9e6798dc7595380314eac7fcfeda14d76
1/*
2 * Copyright (C) 2008 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 static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
20import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
21
22import android.animation.Animator;
23import android.animation.AnimatorSet;
24import android.animation.ObjectAnimator;
25import android.app.ActivityManager;
26import android.content.Context;
27import android.content.res.Resources;
28import android.content.res.Resources.NotFoundException;
29import android.util.AttributeSet;
30import android.util.EventLog;
31import android.util.Log;
32import android.view.MotionEvent;
33import android.view.View;
34import android.view.accessibility.AccessibilityEvent;
35
36import com.android.systemui.EventLogTags;
37import com.android.systemui.R;
38
39public class PhoneStatusBarView extends PanelBar {
40    private static final String TAG = "PhoneStatusBarView";
41    private static final boolean DEBUG = PhoneStatusBar.DEBUG;
42    private static final boolean DEBUG_GESTURES = true;
43
44    PhoneStatusBar mBar;
45    int mScrimColor;
46    float mSettingsPanelDragzoneFrac;
47    float mSettingsPanelDragzoneMin;
48
49    boolean mFullWidthNotifications;
50    PanelView mFadingPanel = null;
51    PanelView mLastFullyOpenedPanel = null;
52    PanelView mNotificationPanel, mSettingsPanel;
53    private boolean mShouldFade;
54    private final StatusBarTransitions mBarTransitions;
55
56    private final class StatusBarTransitions extends BarTransitions {
57        private final int mTransparent;
58        private final float mAlphaWhenOpaque;
59        private final float mAlphaWhenTransparent = 1;
60        private View mLeftSide;
61        private View mRightSide;
62
63        public StatusBarTransitions(Context context) {
64            super(context, PhoneStatusBarView.this);
65            final Resources res = context.getResources();
66            mTransparent = res.getColor(R.color.status_bar_background_transparent);
67            mAlphaWhenOpaque = res.getFraction(R.dimen.status_bar_icon_drawing_alpha, 1, 1);
68        }
69
70        public void init() {
71            mLeftSide = findViewById(R.id.notification_icon_area);
72            mRightSide = findViewById(R.id.system_icon_area);
73            applyMode(getMode(), false /*animate*/);
74        }
75
76        @Override
77        protected Integer getBackgroundColor(int mode) {
78            if (mode == MODE_TRANSPARENT) return mTransparent;
79            return super.getBackgroundColor(mode);
80        }
81
82        public ObjectAnimator animateTransitionTo(View v, float toAlpha) {
83            return ObjectAnimator.ofFloat(v, "alpha", v.getAlpha(), toAlpha);
84        }
85
86        public float getAlphaFor(int mode) {
87            final boolean isTransparent = mode == MODE_SEMI_TRANSPARENT || mode == MODE_TRANSPARENT;
88            return isTransparent ? mAlphaWhenTransparent : mAlphaWhenOpaque;
89        }
90
91        @Override
92        protected void onTransition(int oldMode, int newMode, boolean animate) {
93            super.onTransition(oldMode, newMode, animate);
94            applyMode(newMode, animate);
95        }
96
97        private void applyMode(int mode, boolean animate) {
98            if (mLeftSide == null || mRightSide == null) return;
99            float newAlpha = getAlphaFor(mode);
100            if (animate) {
101                ObjectAnimator lhs = animateTransitionTo(mLeftSide, newAlpha);
102                ObjectAnimator rhs = animateTransitionTo(mRightSide, newAlpha);
103                AnimatorSet set = new AnimatorSet();
104                set.playTogether(lhs, rhs);
105                set.start();
106            } else {
107                mLeftSide.setAlpha(newAlpha);
108                mRightSide.setAlpha(newAlpha);
109            }
110        }
111    }
112
113    public PhoneStatusBarView(Context context, AttributeSet attrs) {
114        super(context, attrs);
115
116        Resources res = getContext().getResources();
117        mScrimColor = res.getColor(R.color.notification_panel_scrim_color);
118        mSettingsPanelDragzoneMin = res.getDimension(R.dimen.settings_panel_dragzone_min);
119        try {
120            mSettingsPanelDragzoneFrac = res.getFraction(R.dimen.settings_panel_dragzone_fraction, 1, 1);
121        } catch (NotFoundException ex) {
122            mSettingsPanelDragzoneFrac = 0f;
123        }
124        mFullWidthNotifications = mSettingsPanelDragzoneFrac <= 0f;
125        mBarTransitions = new StatusBarTransitions(context);
126    }
127
128    public BarTransitions getBarTransitions() {
129        return mBarTransitions;
130    }
131
132    public void setBar(PhoneStatusBar bar) {
133        mBar = bar;
134    }
135
136    public boolean hasFullWidthNotifications() {
137        return mFullWidthNotifications;
138    }
139
140    @Override
141    public void onAttachedToWindow() {
142        for (PanelView pv : mPanels) {
143            pv.setRubberbandingEnabled(!mFullWidthNotifications);
144        }
145        mBarTransitions.init();
146    }
147
148    @Override
149    public void addPanel(PanelView pv) {
150        super.addPanel(pv);
151        if (pv.getId() == R.id.notification_panel) {
152            mNotificationPanel = pv;
153        } else if (pv.getId() == R.id.settings_panel){
154            mSettingsPanel = pv;
155        }
156        pv.setRubberbandingEnabled(!mFullWidthNotifications);
157    }
158
159    @Override
160    public boolean panelsEnabled() {
161        return mBar.panelsEnabled();
162    }
163
164    @Override
165    public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
166        if (super.onRequestSendAccessibilityEvent(child, event)) {
167            // The status bar is very small so augment the view that the user is touching
168            // with the content of the status bar a whole. This way an accessibility service
169            // may announce the current item as well as the entire content if appropriate.
170            AccessibilityEvent record = AccessibilityEvent.obtain();
171            onInitializeAccessibilityEvent(record);
172            dispatchPopulateAccessibilityEvent(record);
173            event.appendRecord(record);
174            return true;
175        }
176        return false;
177    }
178
179    @Override
180    public PanelView selectPanelForTouch(MotionEvent touch) {
181        final float x = touch.getX();
182        final boolean isLayoutRtl = isLayoutRtl();
183
184        if (mFullWidthNotifications) {
185            // No double swiping. If either panel is open, nothing else can be pulled down.
186            return ((mSettingsPanel == null ? 0 : mSettingsPanel.getExpandedHeight())
187                        + mNotificationPanel.getExpandedHeight() > 0)
188                    ? null
189                    : mNotificationPanel;
190        }
191
192        // We split the status bar into thirds: the left 2/3 are for notifications, and the
193        // right 1/3 for quick settings. If you pull the status bar down a second time you'll
194        // toggle panels no matter where you pull it down.
195
196        final float w = getMeasuredWidth();
197        float region = (w * mSettingsPanelDragzoneFrac);
198
199        if (DEBUG) {
200            Log.v(TAG, String.format(
201                "w=%.1f frac=%.3f region=%.1f min=%.1f x=%.1f w-x=%.1f",
202                w, mSettingsPanelDragzoneFrac, region, mSettingsPanelDragzoneMin, x, (w-x)));
203        }
204
205        if (region < mSettingsPanelDragzoneMin) region = mSettingsPanelDragzoneMin;
206
207        final boolean showSettings = isLayoutRtl ? (x < region) : (w - region < x);
208        return showSettings ? mSettingsPanel : mNotificationPanel;
209    }
210
211    @Override
212    public void onPanelPeeked() {
213        super.onPanelPeeked();
214        mBar.makeExpandedVisible();
215    }
216
217    @Override
218    public void startOpeningPanel(PanelView panel) {
219        super.startOpeningPanel(panel);
220        // we only want to start fading if this is the "first" or "last" panel,
221        // which is kind of tricky to determine
222        mShouldFade = (mFadingPanel == null || mFadingPanel.isFullyExpanded());
223        if (DEBUG) {
224            Log.v(TAG, "start opening: " + panel + " shouldfade=" + mShouldFade);
225        }
226        mFadingPanel = panel;
227    }
228
229    @Override
230    public void onAllPanelsCollapsed() {
231        super.onAllPanelsCollapsed();
232        // give animations time to settle
233        mBar.makeExpandedInvisibleSoon();
234        mFadingPanel = null;
235        mLastFullyOpenedPanel = null;
236    }
237
238    @Override
239    public void onPanelFullyOpened(PanelView openPanel) {
240        super.onPanelFullyOpened(openPanel);
241        if (openPanel != mLastFullyOpenedPanel) {
242            openPanel.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
243        }
244        mFadingPanel = openPanel;
245        mLastFullyOpenedPanel = openPanel;
246        mShouldFade = true; // now you own the fade, mister
247    }
248
249    @Override
250    public boolean onTouchEvent(MotionEvent event) {
251        boolean barConsumedEvent = mBar.interceptTouchEvent(event);
252
253        if (DEBUG_GESTURES) {
254            if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
255                EventLog.writeEvent(EventLogTags.SYSUI_PANELBAR_TOUCH,
256                        event.getActionMasked(), (int) event.getX(), (int) event.getY(),
257                        barConsumedEvent ? 1 : 0);
258            }
259        }
260
261        return barConsumedEvent || super.onTouchEvent(event);
262    }
263
264    @Override
265    public boolean onInterceptTouchEvent(MotionEvent event) {
266        return mBar.interceptTouchEvent(event) || super.onInterceptTouchEvent(event);
267    }
268
269    @Override
270    public void panelExpansionChanged(PanelView panel, float frac) {
271        super.panelExpansionChanged(panel, frac);
272
273        if (DEBUG) {
274            Log.v(TAG, "panelExpansionChanged: f=" + frac);
275        }
276
277        if (panel == mFadingPanel && mScrimColor != 0 && ActivityManager.isHighEndGfx()) {
278            if (mShouldFade) {
279                frac = mPanelExpandedFractionSum; // don't judge me
280                // let's start this 20% of the way down the screen
281                frac = frac * 1.2f - 0.2f;
282                if (frac <= 0) {
283                    mBar.mStatusBarWindow.setBackgroundColor(0);
284                } else {
285                    // woo, special effects
286                    final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f))));
287                    // attenuate background color alpha by k
288                    final int color = (int) ((mScrimColor >>> 24) * k) << 24 | (mScrimColor & 0xFFFFFF);
289                    mBar.mStatusBarWindow.setBackgroundColor(color);
290                }
291            }
292        }
293
294        // fade out the panel as it gets buried into the status bar to avoid overdrawing the
295        // status bar on the last frame of a close animation
296        final int H = mBar.getStatusBarHeight();
297        final float ph = panel.getExpandedHeight() + panel.getPaddingBottom();
298        float alpha = 1f;
299        if (ph < 2*H) {
300            if (ph < H) alpha = 0f;
301            else alpha = (ph - H) / H;
302            alpha = alpha * alpha; // get there faster
303        }
304        if (panel.getAlpha() != alpha) {
305            panel.setAlpha(alpha);
306        }
307
308        mBar.animateHeadsUp(mNotificationPanel == panel, mPanelExpandedFractionSum);
309
310        mBar.updateCarrierLabelVisibility(false);
311    }
312}
313