PhoneStatusBar.java revision b018399a3a2762e95126acbe14397eca15bad772
1/*
2 * Copyright (C) 2010 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
19
20import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
21import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
22import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
23import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
24import static android.app.StatusBarManager.windowStateToString;
25import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
26import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
27import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
28import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
29import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
30import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
31import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
32
33import android.animation.Animator;
34import android.animation.AnimatorListenerAdapter;
35import android.animation.TimeInterpolator;
36import android.annotation.NonNull;
37import android.app.ActivityManager;
38import android.app.ActivityManagerNative;
39import android.app.IActivityManager;
40import android.app.Notification;
41import android.app.PendingIntent;
42import android.app.StatusBarManager;
43import android.content.BroadcastReceiver;
44import android.content.ComponentCallbacks2;
45import android.content.Context;
46import android.content.Intent;
47import android.content.IntentFilter;
48import android.content.res.Configuration;
49import android.content.res.Resources;
50import android.database.ContentObserver;
51import android.graphics.Bitmap;
52import android.graphics.Canvas;
53import android.graphics.ColorFilter;
54import android.graphics.PixelFormat;
55import android.graphics.Point;
56import android.graphics.PointF;
57import android.graphics.PorterDuff;
58import android.graphics.PorterDuffXfermode;
59import android.graphics.Rect;
60import android.graphics.drawable.ColorDrawable;
61import android.graphics.drawable.Drawable;
62import android.inputmethodservice.InputMethodService;
63import android.media.AudioAttributes;
64import android.media.MediaMetadata;
65import android.media.session.MediaController;
66import android.media.session.MediaSession;
67import android.media.session.MediaSessionManager;
68import android.media.session.PlaybackState;
69import android.os.AsyncTask;
70import android.os.Bundle;
71import android.os.Handler;
72import android.os.HandlerThread;
73import android.os.IBinder;
74import android.os.Message;
75import android.os.PowerManager;
76import android.os.Process;
77import android.os.RemoteException;
78import android.os.SystemClock;
79import android.os.UserHandle;
80import android.os.UserManager;
81import android.provider.Settings;
82import android.service.notification.NotificationListenerService;
83import android.service.notification.NotificationListenerService.RankingMap;
84import android.service.notification.StatusBarNotification;
85import android.text.TextUtils;
86import android.util.ArraySet;
87import android.util.DisplayMetrics;
88import android.util.EventLog;
89import android.util.Log;
90import android.view.Display;
91import android.view.Gravity;
92import android.view.HardwareCanvas;
93import android.view.KeyEvent;
94import android.view.LayoutInflater;
95import android.view.MotionEvent;
96import android.view.VelocityTracker;
97import android.view.View;
98import android.view.ViewGroup;
99import android.view.ViewGroup.LayoutParams;
100import android.view.ViewPropertyAnimator;
101import android.view.ViewStub;
102import android.view.ViewTreeObserver;
103import android.view.WindowManager;
104import android.view.WindowManagerGlobal;
105import android.view.accessibility.AccessibilityEvent;
106import android.view.animation.AccelerateDecelerateInterpolator;
107import android.view.animation.AccelerateInterpolator;
108import android.view.animation.Animation;
109import android.view.animation.AnimationUtils;
110import android.view.animation.DecelerateInterpolator;
111import android.view.animation.Interpolator;
112import android.view.animation.LinearInterpolator;
113import android.view.animation.PathInterpolator;
114import android.widget.FrameLayout;
115import android.widget.ImageView;
116import android.widget.LinearLayout;
117import android.widget.TextView;
118
119import com.android.internal.statusbar.StatusBarIcon;
120import com.android.keyguard.KeyguardHostView.OnDismissAction;
121import com.android.keyguard.ViewMediatorCallback;
122import com.android.systemui.BatteryMeterView;
123import com.android.systemui.DemoMode;
124import com.android.systemui.EventLogConstants;
125import com.android.systemui.EventLogTags;
126import com.android.systemui.FontSizeUtils;
127import com.android.systemui.R;
128import com.android.systemui.doze.DozeHost;
129import com.android.systemui.doze.DozeLog;
130import com.android.systemui.keyguard.KeyguardViewMediator;
131import com.android.systemui.qs.QSPanel;
132import com.android.systemui.recent.ScreenPinningRequest;
133import com.android.systemui.statusbar.ActivatableNotificationView;
134import com.android.systemui.statusbar.BackDropView;
135import com.android.systemui.statusbar.BaseStatusBar;
136import com.android.systemui.statusbar.CommandQueue;
137import com.android.systemui.statusbar.DismissView;
138import com.android.systemui.statusbar.DragDownHelper;
139import com.android.systemui.statusbar.EmptyShadeView;
140import com.android.systemui.statusbar.ExpandableNotificationRow;
141import com.android.systemui.statusbar.GestureRecorder;
142import com.android.systemui.statusbar.KeyguardIndicationController;
143import com.android.systemui.statusbar.NotificationData;
144import com.android.systemui.statusbar.NotificationData.Entry;
145import com.android.systemui.statusbar.NotificationOverflowContainer;
146import com.android.systemui.statusbar.ScrimView;
147import com.android.systemui.statusbar.SignalClusterView;
148import com.android.systemui.statusbar.SpeedBumpView;
149import com.android.systemui.statusbar.StatusBarIconView;
150import com.android.systemui.statusbar.StatusBarState;
151import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
152import com.android.systemui.statusbar.policy.AccessibilityController;
153import com.android.systemui.statusbar.policy.BatteryController;
154import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
155import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
156import com.android.systemui.statusbar.policy.BrightnessMirrorController;
157import com.android.systemui.statusbar.policy.CastControllerImpl;
158import com.android.systemui.statusbar.policy.FlashlightController;
159import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
160import com.android.systemui.statusbar.policy.HotspotControllerImpl;
161import com.android.systemui.statusbar.policy.KeyButtonView;
162import com.android.systemui.statusbar.policy.KeyguardMonitor;
163import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
164import com.android.systemui.statusbar.policy.LocationControllerImpl;
165import com.android.systemui.statusbar.policy.NetworkControllerImpl;
166import com.android.systemui.statusbar.policy.NextAlarmController;
167import com.android.systemui.statusbar.policy.PreviewInflater;
168import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
169import com.android.systemui.statusbar.policy.SecurityControllerImpl;
170import com.android.systemui.statusbar.policy.UserInfoController;
171import com.android.systemui.statusbar.policy.UserSwitcherController;
172import com.android.systemui.statusbar.policy.ZenModeController;
173import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
174import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
175import com.android.systemui.statusbar.stack.StackScrollAlgorithm;
176import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
177import com.android.systemui.volume.VolumeComponent;
178
179import java.io.FileDescriptor;
180import java.io.PrintWriter;
181import java.util.ArrayList;
182import java.util.Collection;
183import java.util.Collections;
184import java.util.List;
185import java.util.Map;
186
187public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
188        DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener {
189    static final String TAG = "PhoneStatusBar";
190    public static final boolean DEBUG = BaseStatusBar.DEBUG;
191    public static final boolean SPEW = false;
192    public static final boolean DUMPTRUCK = true; // extra dumpsys info
193    public static final boolean DEBUG_GESTURES = false;
194    public static final boolean DEBUG_MEDIA = false;
195    public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
196
197    public static final boolean DEBUG_WINDOW_STATE = false;
198
199    // additional instrumentation for testing purposes; intended to be left on during development
200    public static final boolean CHATTY = DEBUG;
201
202    public static final String ACTION_STATUSBAR_START
203            = "com.android.internal.policy.statusbar.START";
204
205    public static final boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true;
206
207    private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
208    private static final int MSG_CLOSE_PANELS = 1001;
209    private static final int MSG_OPEN_SETTINGS_PANEL = 1002;
210    private static final int MSG_LAUNCH_TRANSITION_TIMEOUT = 1003;
211    // 1020-1040 reserved for BaseStatusBar
212
213    // Time after we abort the launch transition.
214    private static final long LAUNCH_TRANSITION_TIMEOUT_MS = 5000;
215
216    private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
217
218    private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
219    private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
220
221    private static final int STATUS_OR_NAV_TRANSIENT =
222            View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
223    private static final long AUTOHIDE_TIMEOUT_MS = 3000;
224
225    /** The minimum delay in ms between reports of notification visibility. */
226    private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
227
228    /**
229     * The delay to reset the hint text when the hint animation is finished running.
230     */
231    private static final int HINT_RESET_DELAY_MS = 1200;
232
233    private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
234            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
235            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
236            .build();
237
238    public static final int FADE_KEYGUARD_START_DELAY = 100;
239    public static final int FADE_KEYGUARD_DURATION = 300;
240
241    /** Allow some time inbetween the long press for back and recents. */
242    private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
243
244    PhoneStatusBarPolicy mIconPolicy;
245
246    // These are no longer handled by the policy, because we need custom strategies for them
247    BluetoothControllerImpl mBluetoothController;
248    SecurityControllerImpl mSecurityController;
249    BatteryController mBatteryController;
250    LocationControllerImpl mLocationController;
251    NetworkControllerImpl mNetworkController;
252    HotspotControllerImpl mHotspotController;
253    RotationLockControllerImpl mRotationLockController;
254    UserInfoController mUserInfoController;
255    ZenModeController mZenModeController;
256    CastControllerImpl mCastController;
257    VolumeComponent mVolumeComponent;
258    KeyguardUserSwitcher mKeyguardUserSwitcher;
259    FlashlightController mFlashlightController;
260    UserSwitcherController mUserSwitcherController;
261    NextAlarmController mNextAlarmController;
262    KeyguardMonitor mKeyguardMonitor;
263    BrightnessMirrorController mBrightnessMirrorController;
264    AccessibilityController mAccessibilityController;
265
266    int mNaturalBarHeight = -1;
267    int mIconSize = -1;
268    int mIconHPadding = -1;
269    Display mDisplay;
270    Point mCurrentDisplaySize = new Point();
271
272    StatusBarWindowView mStatusBarWindow;
273    PhoneStatusBarView mStatusBarView;
274    private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
275    private StatusBarWindowManager mStatusBarWindowManager;
276    private UnlockMethodCache mUnlockMethodCache;
277    private DozeServiceHost mDozeServiceHost;
278    private boolean mScreenOnComingFromTouch;
279    private PointF mScreenOnTouchLocation;
280
281    int mPixelFormat;
282    Object mQueueLock = new Object();
283
284    // viewgroup containing the normal contents of the statusbar
285    LinearLayout mStatusBarContents;
286
287    // right-hand icons
288    LinearLayout mSystemIconArea;
289    LinearLayout mSystemIcons;
290
291    // left-hand icons
292    LinearLayout mStatusIcons;
293    LinearLayout mStatusIconsKeyguard;
294
295    // the icons themselves
296    IconMerger mNotificationIcons;
297    View mNotificationIconArea;
298
299    // [+>
300    View mMoreIcon;
301
302    // expanded notifications
303    NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
304    View mExpandedContents;
305    int mNotificationPanelGravity;
306    int mNotificationPanelMarginBottomPx;
307    float mNotificationPanelMinHeightFrac;
308    TextView mNotificationPanelDebugText;
309
310    // settings
311    View mFlipSettingsView;
312    private QSPanel mQSPanel;
313
314    // top bar
315    StatusBarHeaderView mHeader;
316    KeyguardStatusBarView mKeyguardStatusBar;
317    View mKeyguardStatusView;
318    KeyguardBottomAreaView mKeyguardBottomArea;
319    boolean mLeaveOpenOnKeyguardHide;
320    KeyguardIndicationController mKeyguardIndicationController;
321
322    private boolean mKeyguardFadingAway;
323    private long mKeyguardFadingAwayDelay;
324    private long mKeyguardFadingAwayDuration;
325
326    int mKeyguardMaxNotificationCount;
327
328    // carrier/wifi label
329    private TextView mCarrierLabel;
330    private boolean mCarrierLabelVisible = false;
331    private int mCarrierLabelHeight;
332    private int mStatusBarHeaderHeight;
333
334    private boolean mShowCarrierInPanel = false;
335
336    // position
337    int[] mPositionTmp = new int[2];
338    boolean mExpandedVisible;
339
340    private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
341
342    // the tracker view
343    int mTrackingPosition; // the position of the top of the tracking view.
344
345    // ticker
346    private boolean mTickerEnabled;
347    private Ticker mTicker;
348    private View mTickerView;
349    private boolean mTicking;
350
351    // Tracking finger for opening/closing.
352    int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
353    boolean mTracking;
354    VelocityTracker mVelocityTracker;
355
356    int[] mAbsPos = new int[2];
357    ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
358
359    // for disabling the status bar
360    int mDisabled = 0;
361
362    // tracking calls to View.setSystemUiVisibility()
363    int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
364
365    DisplayMetrics mDisplayMetrics = new DisplayMetrics();
366
367    // XXX: gesture research
368    private final GestureRecorder mGestureRec = DEBUG_GESTURES
369        ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
370        : null;
371
372    private ScreenPinningRequest mScreenPinningRequest;
373
374    private int mNavigationIconHints = 0;
375    private HandlerThread mHandlerThread;
376
377    // ensure quick settings is disabled until the current user makes it through the setup wizard
378    private boolean mUserSetup = false;
379    private ContentObserver mUserSetupObserver = new ContentObserver(new Handler()) {
380        @Override
381        public void onChange(boolean selfChange) {
382            final boolean userSetup = 0 != Settings.Secure.getIntForUser(
383                    mContext.getContentResolver(),
384                    Settings.Secure.USER_SETUP_COMPLETE,
385                    0 /*default */,
386                    mCurrentUserId);
387            if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " +
388                    "selfChange=%s userSetup=%s mUserSetup=%s",
389                    selfChange, userSetup, mUserSetup));
390
391            if (userSetup != mUserSetup) {
392                mUserSetup = userSetup;
393                if (!mUserSetup && mStatusBarView != null)
394                    animateCollapseQuickSettings();
395            }
396        }
397    };
398
399    final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) {
400        @Override
401        public void onChange(boolean selfChange) {
402            boolean wasUsing = mUseHeadsUp;
403            mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
404                    && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
405                    mContext.getContentResolver(), Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
406                    Settings.Global.HEADS_UP_OFF);
407            mHeadsUpTicker = mUseHeadsUp && 0 != Settings.Global.getInt(
408                    mContext.getContentResolver(), SETTING_HEADS_UP_TICKER, 0);
409            Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
410            if (wasUsing != mUseHeadsUp) {
411                if (!mUseHeadsUp) {
412                    Log.d(TAG, "dismissing any existing heads up notification on disable event");
413                    setHeadsUpVisibility(false);
414                    mHeadsUpNotificationView.release();
415                    removeHeadsUpView();
416                } else {
417                    addHeadsUpView();
418                }
419            }
420        }
421    };
422
423    private int mInteractingWindows;
424    private boolean mAutohideSuspended;
425    private int mStatusBarMode;
426    private int mNavigationBarMode;
427
428    private ViewMediatorCallback mKeyguardViewMediatorCallback;
429    private ScrimController mScrimController;
430    private DozeScrimController mDozeScrimController;
431
432    private final Runnable mAutohide = new Runnable() {
433        @Override
434        public void run() {
435            int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT;
436            if (mSystemUiVisibility != requested) {
437                notifyUiVisibilityChanged(requested);
438            }
439        }};
440
441    private boolean mWaitingForKeyguardExit;
442    private boolean mDozing;
443    private boolean mScrimSrcModeEnabled;
444
445    private Interpolator mLinearOutSlowIn;
446    private Interpolator mLinearInterpolator = new LinearInterpolator();
447    private Interpolator mBackdropInterpolator = new AccelerateDecelerateInterpolator();
448    public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
449    public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
450
451    private BackDropView mBackdrop;
452    private ImageView mBackdropFront, mBackdropBack;
453    private PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
454    private PorterDuffXfermode mSrcOverXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
455
456    private MediaSessionManager mMediaSessionManager;
457    private MediaController mMediaController;
458    private String mMediaNotificationKey;
459    private MediaMetadata mMediaMetadata;
460    private MediaController.Callback mMediaListener
461            = new MediaController.Callback() {
462        @Override
463        public void onPlaybackStateChanged(PlaybackState state) {
464            super.onPlaybackStateChanged(state);
465            if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state);
466        }
467
468        @Override
469        public void onMetadataChanged(MediaMetadata metadata) {
470            super.onMetadataChanged(metadata);
471            if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
472            mMediaMetadata = metadata;
473            updateMediaMetaData(true);
474        }
475    };
476
477    private final OnChildLocationsChangedListener mOnChildLocationsChangedListener =
478            new OnChildLocationsChangedListener() {
479        @Override
480        public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) {
481            userActivity();
482        }
483    };
484
485    private int mDisabledUnmodified;
486
487    /** Keys of notifications currently visible to the user. */
488    private final ArraySet<String> mCurrentlyVisibleNotifications = new ArraySet<String>();
489    private long mLastVisibilityReportUptimeMs;
490
491    private final ShadeUpdates mShadeUpdates = new ShadeUpdates();
492
493    private int mDrawCount;
494    private Runnable mLaunchTransitionEndRunnable;
495    private boolean mLaunchTransitionFadingAway;
496    private ExpandableNotificationRow mDraggedDownRow;
497
498    // Fingerprint (as computed by getLoggingFingerprint() of the last logged state.
499    private int mLastLoggedStateFingerprint;
500
501    private static final int VISIBLE_LOCATIONS = ViewState.LOCATION_FIRST_CARD
502            | ViewState.LOCATION_TOP_STACK_PEEKING
503            | ViewState.LOCATION_MAIN_AREA
504            | ViewState.LOCATION_BOTTOM_STACK_PEEKING;
505
506    private final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
507            new OnChildLocationsChangedListener() {
508                @Override
509                public void onChildLocationsChanged(
510                        NotificationStackScrollLayout stackScrollLayout) {
511                    if (mHandler.hasCallbacks(mVisibilityReporter)) {
512                        // Visibilities will be reported when the existing
513                        // callback is executed.
514                        return;
515                    }
516                    // Calculate when we're allowed to run the visibility
517                    // reporter. Note that this timestamp might already have
518                    // passed. That's OK, the callback will just be executed
519                    // ASAP.
520                    long nextReportUptimeMs =
521                            mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS;
522                    mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs);
523                }
524            };
525
526    // Tracks notifications currently visible in mNotificationStackScroller and
527    // emits visibility events via NoMan on changes.
528    private final Runnable mVisibilityReporter = new Runnable() {
529        private final ArrayList<String> mTmpNewlyVisibleNotifications = new ArrayList<String>();
530        private final ArrayList<String> mTmpCurrentlyVisibleNotifications = new ArrayList<String>();
531
532        @Override
533        public void run() {
534            mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
535
536            // 1. Loop over mNotificationData entries:
537            //   A. Keep list of visible notifications.
538            //   B. Keep list of previously hidden, now visible notifications.
539            // 2. Compute no-longer visible notifications by removing currently
540            //    visible notifications from the set of previously visible
541            //    notifications.
542            // 3. Report newly visible and no-longer visible notifications.
543            // 4. Keep currently visible notifications for next report.
544            ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
545            int N = activeNotifications.size();
546            for (int i = 0; i < N; i++) {
547                Entry entry = activeNotifications.get(i);
548                String key = entry.notification.getKey();
549                boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(key);
550                boolean currentlyVisible =
551                        (mStackScroller.getChildLocation(entry.row) & VISIBLE_LOCATIONS) != 0;
552                if (currentlyVisible) {
553                    // Build new set of visible notifications.
554                    mTmpCurrentlyVisibleNotifications.add(key);
555                }
556                if (!previouslyVisible && currentlyVisible) {
557                    mTmpNewlyVisibleNotifications.add(key);
558                }
559            }
560            ArraySet<String> noLongerVisibleNotifications = mCurrentlyVisibleNotifications;
561            noLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications);
562
563            logNotificationVisibilityChanges(
564                    mTmpNewlyVisibleNotifications, noLongerVisibleNotifications);
565
566            mCurrentlyVisibleNotifications.clear();
567            mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications);
568
569            mTmpNewlyVisibleNotifications.clear();
570            mTmpCurrentlyVisibleNotifications.clear();
571        }
572    };
573
574    private final View.OnClickListener mOverflowClickListener = new View.OnClickListener() {
575        @Override
576        public void onClick(View v) {
577            goToLockedShade(null);
578        }
579    };
580
581    @Override
582    public void start() {
583        mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
584                .getDefaultDisplay();
585        updateDisplaySize();
586        mScrimSrcModeEnabled = mContext.getResources().getBoolean(
587                R.bool.config_status_bar_scrim_behind_use_src);
588        super.start(); // calls createAndAddWindows()
589
590        mMediaSessionManager
591                = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
592        // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
593        // in session state
594
595        addNavigationBar();
596
597        // Lastly, call to the icon policy to install/update all the icons.
598        mIconPolicy = new PhoneStatusBarPolicy(mContext, mCastController, mHotspotController);
599        mSettingsObserver.onChange(false); // set up
600
601        mHeadsUpObserver.onChange(true); // set up
602        if (ENABLE_HEADS_UP) {
603            mContext.getContentResolver().registerContentObserver(
604                    Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true,
605                    mHeadsUpObserver);
606            mContext.getContentResolver().registerContentObserver(
607                    Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
608                    mHeadsUpObserver);
609        }
610        mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
611        mUnlockMethodCache.addListener(this);
612        startKeyguard();
613
614        mDozeServiceHost = new DozeServiceHost();
615        putComponent(DozeHost.class, mDozeServiceHost);
616        putComponent(PhoneStatusBar.class, this);
617
618        setControllerUsers();
619
620        notifyUserAboutHiddenNotifications();
621
622        mScreenPinningRequest = new ScreenPinningRequest(mContext);
623    }
624
625    // ================================================================================
626    // Constructing the view
627    // ================================================================================
628    protected PhoneStatusBarView makeStatusBarView() {
629        final Context context = mContext;
630
631        Resources res = context.getResources();
632
633        updateDisplaySize(); // populates mDisplayMetrics
634        updateResources();
635
636        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
637
638        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
639                R.layout.super_status_bar, null);
640        mStatusBarWindow.mService = this;
641        mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
642            @Override
643            public boolean onTouch(View v, MotionEvent event) {
644                checkUserAutohide(v, event);
645                if (event.getAction() == MotionEvent.ACTION_DOWN) {
646                    if (mExpandedVisible) {
647                        animateCollapsePanels();
648                    }
649                }
650                return mStatusBarWindow.onTouchEvent(event);
651            }});
652
653        mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
654        mStatusBarView.setBar(this);
655
656        PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
657        mStatusBarView.setPanelHolder(holder);
658
659        mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
660                R.id.notification_panel);
661        mNotificationPanel.setStatusBar(this);
662
663        if (!ActivityManager.isHighEndGfx()) {
664            mStatusBarWindow.setBackground(null);
665            mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
666                    R.color.notification_panel_solid_background)));
667        }
668        if (ENABLE_HEADS_UP) {
669            mHeadsUpNotificationView =
670                    (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null);
671            mHeadsUpNotificationView.setVisibility(View.GONE);
672            mHeadsUpNotificationView.setBar(this);
673        }
674        if (MULTIUSER_DEBUG) {
675            mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(
676                    R.id.header_debug_info);
677            mNotificationPanelDebugText.setVisibility(View.VISIBLE);
678        }
679
680        updateShowSearchHoldoff();
681
682        try {
683            boolean showNav = mWindowManagerService.hasNavigationBar();
684            if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
685            if (showNav) {
686                mNavigationBarView =
687                    (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
688
689                mNavigationBarView.setDisabledFlags(mDisabled);
690                mNavigationBarView.setBar(this);
691                mNavigationBarView.setOnVerticalChangedListener(
692                        new NavigationBarView.OnVerticalChangedListener() {
693                    @Override
694                    public void onVerticalChanged(boolean isVertical) {
695                        if (mSearchPanelView != null) {
696                            mSearchPanelView.setHorizontal(isVertical);
697                        }
698                        mNotificationPanel.setQsScrimEnabled(!isVertical);
699                    }
700                });
701                mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
702                    @Override
703                    public boolean onTouch(View v, MotionEvent event) {
704                        checkUserAutohide(v, event);
705                        return false;
706                    }});
707            }
708        } catch (RemoteException ex) {
709            // no window manager? good luck with that
710        }
711
712        // figure out which pixel-format to use for the status bar.
713        mPixelFormat = PixelFormat.OPAQUE;
714
715        mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
716        mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons);
717        mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
718        mNotificationIconArea = mStatusBarView.findViewById(R.id.notification_icon_area_inner);
719        mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
720        mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
721        mNotificationIcons.setOverflowIndicator(mMoreIcon);
722        mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
723
724        mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
725                R.id.notification_stack_scroller);
726        mStackScroller.setLongPressListener(getNotificationLongClicker());
727        mStackScroller.setPhoneStatusBar(this);
728
729        mKeyguardIconOverflowContainer =
730                (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
731                        R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
732        mKeyguardIconOverflowContainer.setOnActivatedListener(this);
733        mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
734        mStackScroller.addView(mKeyguardIconOverflowContainer);
735
736        SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate(
737                        R.layout.status_bar_notification_speed_bump, mStackScroller, false);
738        mStackScroller.setSpeedBumpView(speedBump);
739        mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
740                R.layout.status_bar_no_notifications, mStackScroller, false);
741        mStackScroller.setEmptyShadeView(mEmptyShadeView);
742        mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
743                R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
744        mDismissView.setOnButtonClickListener(new View.OnClickListener() {
745            @Override
746            public void onClick(View v) {
747                clearAllNotifications();
748            }
749        });
750        mStackScroller.setDismissView(mDismissView);
751        mExpandedContents = mStackScroller;
752
753        mBackdrop = (BackDropView) mStatusBarWindow.findViewById(R.id.backdrop);
754        mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front);
755        mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back);
756
757        ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind);
758        ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front);
759        mScrimController = new ScrimController(scrimBehind, scrimInFront, mScrimSrcModeEnabled);
760        mScrimController.setBackDropView(mBackdrop);
761        mStatusBarView.setScrimController(mScrimController);
762        mDozeScrimController = new DozeScrimController(mScrimController, context);
763
764        mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
765        mHeader.setActivityStarter(this);
766        mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
767        mStatusIconsKeyguard = (LinearLayout) mKeyguardStatusBar.findViewById(R.id.statusIcons);
768        mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
769        mKeyguardBottomArea =
770                (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
771        mKeyguardBottomArea.setActivityStarter(this);
772        mKeyguardIndicationController = new KeyguardIndicationController(mContext,
773                (KeyguardIndicationTextView) mStatusBarWindow.findViewById(
774                        R.id.keyguard_indication_text));
775        mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController);
776
777        mTickerEnabled = res.getBoolean(R.bool.enable_ticker);
778        if (mTickerEnabled) {
779            final ViewStub tickerStub = (ViewStub) mStatusBarView.findViewById(R.id.ticker_stub);
780            if (tickerStub != null) {
781                mTickerView = tickerStub.inflate();
782                mTicker = new MyTicker(context, mStatusBarView);
783
784                TickerView tickerView = (TickerView) mStatusBarView.findViewById(R.id.tickerText);
785                tickerView.mTicker = mTicker;
786            }
787        }
788
789        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
790
791        // set the inital view visibility
792        setAreThereNotifications();
793
794        // Background thread for any controllers that need it.
795        mHandlerThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
796        mHandlerThread.start();
797
798        // Other icons
799        mLocationController = new LocationControllerImpl(mContext); // will post a notification
800        mBatteryController = new BatteryController(mContext);
801        mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {
802            @Override
803            public void onPowerSaveChanged() {
804                mHandler.post(mCheckBarModes);
805                if (mDozeServiceHost != null) {
806                    mDozeServiceHost.firePowerSaveChanged(mBatteryController.isPowerSave());
807                }
808            }
809            @Override
810            public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
811                // noop
812            }
813        });
814        mNetworkController = new NetworkControllerImpl(mContext);
815        mHotspotController = new HotspotControllerImpl(mContext);
816        mBluetoothController = new BluetoothControllerImpl(mContext, mHandlerThread.getLooper());
817        mSecurityController = new SecurityControllerImpl(mContext);
818        if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) {
819            mRotationLockController = new RotationLockControllerImpl(mContext);
820        }
821        mUserInfoController = new UserInfoController(mContext);
822        mVolumeComponent = getComponent(VolumeComponent.class);
823        if (mVolumeComponent != null) {
824            mZenModeController = mVolumeComponent.getZenController();
825        }
826        mCastController = new CastControllerImpl(mContext);
827        final SignalClusterView signalCluster =
828                (SignalClusterView) mStatusBarView.findViewById(R.id.signal_cluster);
829        final SignalClusterView signalClusterKeyguard =
830                (SignalClusterView) mKeyguardStatusBar.findViewById(R.id.signal_cluster);
831        final SignalClusterView signalClusterQs =
832                (SignalClusterView) mHeader.findViewById(R.id.signal_cluster);
833        mNetworkController.addSignalCluster(signalCluster);
834        mNetworkController.addSignalCluster(signalClusterKeyguard);
835        mNetworkController.addSignalCluster(signalClusterQs);
836        signalCluster.setSecurityController(mSecurityController);
837        signalCluster.setNetworkController(mNetworkController);
838        signalClusterKeyguard.setSecurityController(mSecurityController);
839        signalClusterKeyguard.setNetworkController(mNetworkController);
840        signalClusterQs.setSecurityController(mSecurityController);
841        signalClusterQs.setNetworkController(mNetworkController);
842        final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();
843        if (isAPhone) {
844            mNetworkController.addEmergencyListener(new NetworkControllerImpl.EmergencyListener() {
845                @Override
846                public void setEmergencyCallsOnly(boolean emergencyOnly) {
847                    mHeader.setShowEmergencyCallsOnly(emergencyOnly);
848                }
849            });
850        }
851
852        mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
853        mShowCarrierInPanel = (mCarrierLabel != null);
854        if (DEBUG) Log.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel);
855        if (mShowCarrierInPanel) {
856            mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
857
858            mNetworkController.addCarrierLabel(new NetworkControllerImpl.CarrierLabelListener() {
859                @Override
860                public void setCarrierLabel(String label) {
861                    mCarrierLabel.setText(label);
862                    if (mNetworkController.hasMobileDataFeature()) {
863                        if (TextUtils.isEmpty(label)) {
864                            mCarrierLabel.setVisibility(View.GONE);
865                        } else {
866                            mCarrierLabel.setVisibility(View.VISIBLE);
867                        }
868                    }
869                }
870            });
871        }
872
873        mFlashlightController = new FlashlightController(mContext);
874        mKeyguardBottomArea.setFlashlightController(mFlashlightController);
875        mKeyguardBottomArea.setPhoneStatusBar(this);
876        mAccessibilityController = new AccessibilityController(mContext);
877        mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
878        mNextAlarmController = new NextAlarmController(mContext);
879        mKeyguardMonitor = new KeyguardMonitor();
880        if (UserSwitcherController.isUserSwitcherAvailable(UserManager.get(mContext))) {
881            mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor);
882        }
883        mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
884                (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
885                mKeyguardStatusBar, mNotificationPanel, mUserSwitcherController);
886
887
888        // Set up the quick settings tile panel
889        mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
890        if (mQSPanel != null) {
891            final QSTileHost qsh = new QSTileHost(mContext, this,
892                    mBluetoothController, mLocationController, mRotationLockController,
893                    mNetworkController, mZenModeController, mHotspotController,
894                    mCastController, mFlashlightController,
895                    mUserSwitcherController, mKeyguardMonitor,
896                    mSecurityController);
897            mQSPanel.setHost(qsh);
898            mQSPanel.setTiles(qsh.getTiles());
899            mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);
900            mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
901            mHeader.setQSPanel(mQSPanel);
902            qsh.setCallback(new QSTileHost.Callback() {
903                @Override
904                public void onTilesChanged() {
905                    mQSPanel.setTiles(qsh.getTiles());
906                }
907            });
908        }
909
910        // User info. Trigger first load.
911        mHeader.setUserInfoController(mUserInfoController);
912        mKeyguardStatusBar.setUserInfoController(mUserInfoController);
913        mUserInfoController.reloadUserInfo();
914
915        mHeader.setBatteryController(mBatteryController);
916        ((BatteryMeterView) mStatusBarView.findViewById(R.id.battery)).setBatteryController(
917                mBatteryController);
918        mKeyguardStatusBar.setBatteryController(mBatteryController);
919        mHeader.setNextAlarmController(mNextAlarmController);
920
921        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
922        mBroadcastReceiver.onReceive(mContext,
923                new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
924
925        // receive broadcasts
926        IntentFilter filter = new IntentFilter();
927        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
928        filter.addAction(Intent.ACTION_SCREEN_OFF);
929        filter.addAction(Intent.ACTION_SCREEN_ON);
930        if (DEBUG_MEDIA_FAKE_ARTWORK) {
931            filter.addAction("fake_artwork");
932        }
933        filter.addAction(ACTION_DEMO);
934        context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
935
936        // listen for USER_SETUP_COMPLETE setting (per-user)
937        resetUserSetupObserver();
938
939        startGlyphRasterizeHack();
940        return mStatusBarView;
941    }
942
943    private void clearAllNotifications() {
944
945        // animate-swipe all dismissable notifications, then animate the shade closed
946        int numChildren = mStackScroller.getChildCount();
947
948        final ArrayList<View> viewsToHide = new ArrayList<View>(numChildren);
949        for (int i = 0; i < numChildren; i++) {
950            final View child = mStackScroller.getChildAt(i);
951            if (mStackScroller.canChildBeDismissed(child)) {
952                if (child.getVisibility() == View.VISIBLE) {
953                    viewsToHide.add(child);
954                }
955            }
956        }
957        if (viewsToHide.isEmpty()) {
958            animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
959            return;
960        }
961
962        addPostCollapseAction(new Runnable() {
963            @Override
964            public void run() {
965                try {
966                    mBarService.onClearAllNotifications(mCurrentUserId);
967                } catch (Exception ex) { }
968            }
969        });
970
971        performDismissAllAnimations(viewsToHide);
972
973    }
974
975    private void performDismissAllAnimations(ArrayList<View> hideAnimatedList) {
976        Runnable animationFinishAction = new Runnable() {
977            @Override
978            public void run() {
979                mStackScroller.post(new Runnable() {
980                    @Override
981                    public void run() {
982                        mStackScroller.setDismissAllInProgress(false);
983                    }
984                });
985                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
986            }
987        };
988
989        // let's disable our normal animations
990        mStackScroller.setDismissAllInProgress(true);
991
992        // Decrease the delay for every row we animate to give the sense of
993        // accelerating the swipes
994        int rowDelayDecrement = 10;
995        int currentDelay = 140;
996        int totalDelay = 180;
997        int numItems = hideAnimatedList.size();
998        for (int i = numItems - 1; i >= 0; i--) {
999            View view = hideAnimatedList.get(i);
1000            Runnable endRunnable = null;
1001            if (i == 0) {
1002                endRunnable = animationFinishAction;
1003            }
1004            mStackScroller.dismissViewAnimated(view, endRunnable, totalDelay, 260);
1005            currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
1006            totalDelay += currentDelay;
1007        }
1008    }
1009
1010    /**
1011     * Hack to improve glyph rasterization for scaled text views.
1012     */
1013    private void startGlyphRasterizeHack() {
1014        mStatusBarView.getViewTreeObserver().addOnPreDrawListener(
1015                new ViewTreeObserver.OnPreDrawListener() {
1016            @Override
1017            public boolean onPreDraw() {
1018                if (mDrawCount == 1) {
1019                    mStatusBarView.getViewTreeObserver().removeOnPreDrawListener(this);
1020                    HardwareCanvas.setProperty("extraRasterBucket",
1021                            Float.toString(StackScrollAlgorithm.DIMMED_SCALE));
1022                    HardwareCanvas.setProperty("extraRasterBucket", Float.toString(
1023                            mContext.getResources().getDimensionPixelSize(
1024                                    R.dimen.qs_time_collapsed_size)
1025                            / mContext.getResources().getDimensionPixelSize(
1026                                    R.dimen.qs_time_expanded_size)));
1027                }
1028                mDrawCount++;
1029                return true;
1030            }
1031        });
1032    }
1033
1034    @Override
1035    protected void setZenMode(int mode) {
1036        super.setZenMode(mode);
1037        if (mIconPolicy != null) {
1038            mIconPolicy.setZenMode(mode);
1039        }
1040    }
1041
1042    private void startKeyguard() {
1043        KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
1044        mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
1045                mStatusBarWindow, mStatusBarWindowManager, mScrimController);
1046        mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
1047    }
1048
1049    @Override
1050    protected View getStatusBarView() {
1051        return mStatusBarView;
1052    }
1053
1054    public StatusBarWindowView getStatusBarWindow() {
1055        return mStatusBarWindow;
1056    }
1057
1058    @Override
1059    protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
1060        boolean opaque = false;
1061        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1062                LayoutParams.MATCH_PARENT,
1063                LayoutParams.MATCH_PARENT,
1064                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
1065                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1066                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
1067                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1068                (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
1069        if (ActivityManager.isHighEndGfx()) {
1070            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1071        }
1072        lp.gravity = Gravity.BOTTOM | Gravity.START;
1073        lp.setTitle("SearchPanel");
1074        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
1075        | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
1076        return lp;
1077    }
1078
1079    @Override
1080    protected void updateSearchPanel() {
1081        super.updateSearchPanel();
1082        if (mNavigationBarView != null) {
1083            mNavigationBarView.setDelegateView(mSearchPanelView);
1084        }
1085    }
1086
1087    @Override
1088    public void showSearchPanel() {
1089        super.showSearchPanel();
1090        mHandler.removeCallbacks(mShowSearchPanel);
1091
1092        // we want to freeze the sysui state wherever it is
1093        mSearchPanelView.setSystemUiVisibility(mSystemUiVisibility);
1094
1095        if (mNavigationBarView != null) {
1096            WindowManager.LayoutParams lp =
1097                (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
1098            lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
1099            mWindowManager.updateViewLayout(mNavigationBarView, lp);
1100        }
1101    }
1102
1103    @Override
1104    public void hideSearchPanel() {
1105        super.hideSearchPanel();
1106        if (mNavigationBarView != null) {
1107            WindowManager.LayoutParams lp =
1108                (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
1109            lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
1110            mWindowManager.updateViewLayout(mNavigationBarView, lp);
1111        }
1112    }
1113
1114    public int getStatusBarHeight() {
1115        if (mNaturalBarHeight < 0) {
1116            final Resources res = mContext.getResources();
1117            mNaturalBarHeight =
1118                    res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
1119        }
1120        return mNaturalBarHeight;
1121    }
1122
1123    private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
1124        public void onClick(View v) {
1125            awakenDreams();
1126            toggleRecentApps();
1127        }
1128    };
1129
1130    private long mLastLockToAppLongPress;
1131    private View.OnLongClickListener mLongPressBackRecentsListener =
1132            new View.OnLongClickListener() {
1133        @Override
1134        public boolean onLongClick(View v) {
1135            handleLongPressBackRecents(v);
1136            return true;
1137        }
1138    };
1139
1140    private int mShowSearchHoldoff = 0;
1141    private Runnable mShowSearchPanel = new Runnable() {
1142        public void run() {
1143            showSearchPanel();
1144            awakenDreams();
1145        }
1146    };
1147
1148    View.OnTouchListener mHomeActionListener = new View.OnTouchListener() {
1149        public boolean onTouch(View v, MotionEvent event) {
1150            switch(event.getAction()) {
1151                case MotionEvent.ACTION_DOWN:
1152                if (!shouldDisableNavbarGestures()) {
1153                    mHandler.removeCallbacks(mShowSearchPanel);
1154                    mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
1155                }
1156            break;
1157
1158            case MotionEvent.ACTION_UP:
1159            case MotionEvent.ACTION_CANCEL:
1160                mHandler.removeCallbacks(mShowSearchPanel);
1161                awakenDreams();
1162            break;
1163        }
1164        return false;
1165        }
1166    };
1167
1168    private void awakenDreams() {
1169        if (mDreamManager != null) {
1170            try {
1171                mDreamManager.awaken();
1172            } catch (RemoteException e) {
1173                // fine, stay asleep then
1174            }
1175        }
1176    }
1177
1178    private void prepareNavigationBarView() {
1179        mNavigationBarView.reorient();
1180
1181        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
1182        mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
1183        mNavigationBarView.getRecentsButton().setLongClickable(true);
1184        mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
1185        mNavigationBarView.getBackButton().setLongClickable(true);
1186        mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
1187        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
1188        updateSearchPanel();
1189    }
1190
1191    // For small-screen devices (read: phones) that lack hardware navigation buttons
1192    private void addNavigationBar() {
1193        if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
1194        if (mNavigationBarView == null) return;
1195
1196        prepareNavigationBarView();
1197
1198        mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
1199    }
1200
1201    private void repositionNavigationBar() {
1202        if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
1203
1204        prepareNavigationBarView();
1205
1206        mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams());
1207    }
1208
1209    private void notifyNavigationBarScreenOn(boolean screenOn) {
1210        if (mNavigationBarView == null) return;
1211        mNavigationBarView.notifyScreenOn(screenOn);
1212    }
1213
1214    private WindowManager.LayoutParams getNavigationBarLayoutParams() {
1215        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1216                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
1217                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
1218                    0
1219                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
1220                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1221                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1222                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
1223                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1224                PixelFormat.TRANSLUCENT);
1225        // this will allow the navbar to run in an overlay on devices that support this
1226        if (ActivityManager.isHighEndGfx()) {
1227            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1228        }
1229
1230        lp.setTitle("NavigationBar");
1231        lp.windowAnimations = 0;
1232        return lp;
1233    }
1234
1235    private void addHeadsUpView() {
1236        int headsUpHeight = mContext.getResources()
1237                .getDimensionPixelSize(R.dimen.heads_up_window_height);
1238        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1239                LayoutParams.MATCH_PARENT, headsUpHeight,
1240                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
1241                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1242                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
1243                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1244                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1245                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
1246                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1247                PixelFormat.TRANSLUCENT);
1248        lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1249        lp.gravity = Gravity.TOP;
1250        lp.setTitle("Heads Up");
1251        lp.packageName = mContext.getPackageName();
1252        lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp;
1253
1254        mWindowManager.addView(mHeadsUpNotificationView, lp);
1255    }
1256
1257    private void removeHeadsUpView() {
1258        mWindowManager.removeView(mHeadsUpNotificationView);
1259    }
1260
1261    public void refreshAllStatusBarIcons() {
1262        refreshAllIconsForLayout(mStatusIcons);
1263        refreshAllIconsForLayout(mStatusIconsKeyguard);
1264        refreshAllIconsForLayout(mNotificationIcons);
1265    }
1266
1267    private void refreshAllIconsForLayout(LinearLayout ll) {
1268        final int count = ll.getChildCount();
1269        for (int n = 0; n < count; n++) {
1270            View child = ll.getChildAt(n);
1271            if (child instanceof StatusBarIconView) {
1272                ((StatusBarIconView) child).updateDrawable();
1273            }
1274        }
1275    }
1276
1277    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
1278        if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
1279                + " icon=" + icon);
1280        StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
1281        view.set(icon);
1282        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(
1283                LayoutParams.WRAP_CONTENT, mIconSize));
1284        view = new StatusBarIconView(mContext, slot, null);
1285        view.set(icon);
1286        mStatusIconsKeyguard.addView(view, viewIndex, new LinearLayout.LayoutParams(
1287                LayoutParams.WRAP_CONTENT, mIconSize));
1288    }
1289
1290    public void updateIcon(String slot, int index, int viewIndex,
1291            StatusBarIcon old, StatusBarIcon icon) {
1292        if (SPEW) Log.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
1293                + " old=" + old + " icon=" + icon);
1294        StatusBarIconView view = (StatusBarIconView) mStatusIcons.getChildAt(viewIndex);
1295        view.set(icon);
1296        view = (StatusBarIconView) mStatusIconsKeyguard.getChildAt(viewIndex);
1297        view.set(icon);
1298    }
1299
1300    public void removeIcon(String slot, int index, int viewIndex) {
1301        if (SPEW) Log.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
1302        mStatusIcons.removeViewAt(viewIndex);
1303        mStatusIconsKeyguard.removeViewAt(viewIndex);
1304    }
1305
1306    public UserHandle getCurrentUserHandle() {
1307        return new UserHandle(mCurrentUserId);
1308    }
1309
1310    @Override
1311    public void addNotification(StatusBarNotification notification, RankingMap ranking) {
1312        if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
1313        if (mUseHeadsUp && shouldInterrupt(notification)) {
1314            if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
1315            Entry interruptionCandidate = new Entry(notification, null);
1316            ViewGroup holder = mHeadsUpNotificationView.getHolder();
1317            if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
1318                // 1. Populate mHeadsUpNotificationView
1319                mHeadsUpNotificationView.showNotification(interruptionCandidate);
1320
1321                // do not show the notification in the shade, yet.
1322                return;
1323            }
1324        }
1325
1326        Entry shadeEntry = createNotificationViews(notification);
1327        if (shadeEntry == null) {
1328            return;
1329        }
1330
1331        if (notification.getNotification().fullScreenIntent != null) {
1332            // Stop screensaver if the notification has a full-screen intent.
1333            // (like an incoming phone call)
1334            awakenDreams();
1335
1336            // not immersive & a full-screen alert should be shown
1337            if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
1338            try {
1339                EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
1340                        notification.getKey());
1341                notification.getNotification().fullScreenIntent.send();
1342            } catch (PendingIntent.CanceledException e) {
1343            }
1344        } else {
1345            // usual case: status bar visible & not immersive
1346
1347            // show the ticker if there isn't already a heads up
1348            if (mHeadsUpNotificationView.getEntry() == null) {
1349                tick(notification, true);
1350            }
1351        }
1352        addNotificationViews(shadeEntry, ranking);
1353        // Recalculate the position of the sliding windows and the titles.
1354        setAreThereNotifications();
1355        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1356    }
1357
1358    public void displayNotificationFromHeadsUp(StatusBarNotification notification) {
1359        NotificationData.Entry shadeEntry = createNotificationViews(notification);
1360        if (shadeEntry == null) {
1361            return;
1362        }
1363        shadeEntry.setInterruption();
1364
1365        addNotificationViews(shadeEntry, null);
1366        // Recalculate the position of the sliding windows and the titles.
1367        setAreThereNotifications();
1368        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1369    }
1370
1371    @Override
1372    public void resetHeadsUpDecayTimer() {
1373        mHandler.removeMessages(MSG_DECAY_HEADS_UP);
1374        if (mUseHeadsUp && mHeadsUpNotificationDecay > 0
1375                && mHeadsUpNotificationView.isClearable()) {
1376            mHandler.sendEmptyMessageDelayed(MSG_DECAY_HEADS_UP, mHeadsUpNotificationDecay);
1377        }
1378    }
1379
1380    @Override
1381    public void scheduleHeadsUpOpen() {
1382        mHandler.removeMessages(MSG_SHOW_HEADS_UP);
1383        mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
1384    }
1385
1386    @Override
1387    public void scheduleHeadsUpClose() {
1388        mHandler.removeMessages(MSG_HIDE_HEADS_UP);
1389        mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
1390    }
1391
1392    @Override
1393    public void scheduleHeadsUpEscalation() {
1394        mHandler.removeMessages(MSG_ESCALATE_HEADS_UP);
1395        mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP);
1396    }
1397
1398    @Override
1399    protected void updateNotificationRanking(RankingMap ranking) {
1400        mNotificationData.updateRanking(ranking);
1401        updateNotifications();
1402    }
1403
1404    @Override
1405    public void removeNotification(String key, RankingMap ranking) {
1406        if (ENABLE_HEADS_UP && mHeadsUpNotificationView.getEntry() != null
1407                && key.equals(mHeadsUpNotificationView.getEntry().notification.getKey())) {
1408            mHeadsUpNotificationView.clear();
1409        }
1410
1411        StatusBarNotification old = removeNotificationViews(key, ranking);
1412        if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
1413
1414        if (old != null) {
1415            // Cancel the ticker if it's still running
1416            if (mTickerEnabled) {
1417                mTicker.removeEntry(old);
1418            }
1419
1420            // Recalculate the position of the sliding windows and the titles.
1421            updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1422
1423            if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
1424                    && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
1425                if (mState == StatusBarState.SHADE) {
1426                    animateCollapsePanels();
1427                } else if (mState == StatusBarState.SHADE_LOCKED) {
1428                    goToKeyguard();
1429                }
1430            }
1431        }
1432        setAreThereNotifications();
1433    }
1434
1435    @Override
1436    protected void refreshLayout(int layoutDirection) {
1437        if (mNavigationBarView != null) {
1438            mNavigationBarView.setLayoutDirection(layoutDirection);
1439        }
1440        refreshAllStatusBarIcons();
1441    }
1442
1443    private void updateShowSearchHoldoff() {
1444        mShowSearchHoldoff = mContext.getResources().getInteger(
1445            R.integer.config_show_search_delay);
1446    }
1447
1448    private void updateNotificationShade() {
1449        if (mStackScroller == null) return;
1450
1451        // Do not modify the notifications during collapse.
1452        if (isCollapsing()) {
1453            addPostCollapseAction(new Runnable() {
1454                @Override
1455                public void run() {
1456                    updateNotificationShade();
1457                }
1458            });
1459            return;
1460        }
1461
1462        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1463        ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
1464        final int N = activeNotifications.size();
1465        for (int i=0; i<N; i++) {
1466            Entry ent = activeNotifications.get(i);
1467            int vis = ent.notification.getNotification().visibility;
1468
1469            // Display public version of the notification if we need to redact.
1470            final boolean hideSensitive =
1471                    !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId());
1472            boolean sensitiveNote = vis == Notification.VISIBILITY_PRIVATE;
1473            boolean sensitivePackage = packageHasVisibilityOverride(ent.notification.getKey());
1474            boolean sensitive = (sensitiveNote && hideSensitive) || sensitivePackage;
1475            boolean showingPublic = sensitive && isLockscreenPublicMode();
1476            ent.row.setSensitive(sensitive);
1477            if (ent.autoRedacted && ent.legacy) {
1478                // TODO: Also fade this? Or, maybe easier (and better), provide a dark redacted form
1479                // for legacy auto redacted notifications.
1480                if (showingPublic) {
1481                    ent.row.setShowingLegacyBackground(false);
1482                } else {
1483                    ent.row.setShowingLegacyBackground(true);
1484                }
1485            }
1486            toShow.add(ent.row);
1487        }
1488
1489        ArrayList<View> toRemove = new ArrayList<View>();
1490        for (int i=0; i< mStackScroller.getChildCount(); i++) {
1491            View child = mStackScroller.getChildAt(i);
1492            if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
1493                toRemove.add(child);
1494            }
1495        }
1496
1497        for (View remove : toRemove) {
1498            mStackScroller.removeView(remove);
1499        }
1500        for (int i=0; i<toShow.size(); i++) {
1501            View v = toShow.get(i);
1502            if (v.getParent() == null) {
1503                mStackScroller.addView(v);
1504            }
1505        }
1506
1507        // So after all this work notifications still aren't sorted correctly.
1508        // Let's do that now by advancing through toShow and mStackScroller in
1509        // lock-step, making sure mStackScroller matches what we see in toShow.
1510        int j = 0;
1511        for (int i = 0; i < mStackScroller.getChildCount(); i++) {
1512            View child = mStackScroller.getChildAt(i);
1513            if (!(child instanceof ExpandableNotificationRow)) {
1514                // We don't care about non-notification views.
1515                continue;
1516            }
1517
1518            if (child == toShow.get(j)) {
1519                // Everything is well, advance both lists.
1520                j++;
1521                continue;
1522            }
1523
1524            // Oops, wrong notification at this position. Put the right one
1525            // here and advance both lists.
1526            mStackScroller.changeViewPosition(toShow.get(j), i);
1527            j++;
1528        }
1529        updateRowStates();
1530        updateSpeedbump();
1531        updateClearAll();
1532        updateEmptyShadeView();
1533
1534        // Disable QS if device not provisioned.
1535        // If the user switcher is simple then disable QS during setup because
1536        // the user intends to use the lock screen user switcher, QS in not needed.
1537        mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned()
1538                && (mUserSetup || mUserSwitcherController == null
1539                        || !mUserSwitcherController.isSimpleUserSwitcher()));
1540        mShadeUpdates.check();
1541    }
1542
1543    private boolean packageHasVisibilityOverride(String key) {
1544        return mNotificationData.getVisibilityOverride(key)
1545                != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
1546    }
1547
1548    private void updateClearAll() {
1549        boolean showDismissView =
1550                mState != StatusBarState.KEYGUARD &&
1551                mNotificationData.hasActiveClearableNotifications();
1552        mStackScroller.updateDismissView(showDismissView);
1553    }
1554
1555    private void updateEmptyShadeView() {
1556        boolean showEmptyShade =
1557                mState != StatusBarState.KEYGUARD &&
1558                        mNotificationData.getActiveNotifications().size() == 0;
1559        mNotificationPanel.setShadeEmpty(showEmptyShade);
1560    }
1561
1562    private void updateSpeedbump() {
1563        int speedbumpIndex = -1;
1564        int currentIndex = 0;
1565        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1566        final int N = activeNotifications.size();
1567        for (int i = 0; i < N; i++) {
1568            Entry entry = activeNotifications.get(i);
1569            if (entry.row.getVisibility() != View.GONE &&
1570                    mNotificationData.isAmbient(entry.key)) {
1571                speedbumpIndex = currentIndex;
1572                break;
1573            }
1574            currentIndex++;
1575        }
1576        mStackScroller.updateSpeedBumpIndex(speedbumpIndex);
1577    }
1578
1579    @Override
1580    protected void updateNotifications() {
1581        // TODO: Move this into updateNotificationIcons()?
1582        if (mNotificationIcons == null) return;
1583
1584        mNotificationData.filterAndSort();
1585
1586        updateNotificationShade();
1587        updateNotificationIcons();
1588    }
1589
1590    private void updateNotificationIcons() {
1591        final LinearLayout.LayoutParams params
1592            = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
1593
1594        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1595        final int N = activeNotifications.size();
1596        ArrayList<StatusBarIconView> toShow = new ArrayList<>(N);
1597
1598        // Filter out notifications with low scores.
1599        for (int i = 0; i < N; i++) {
1600            Entry ent = activeNotifications.get(i);
1601            if (ent.notification.getScore() < HIDE_ICONS_BELOW_SCORE &&
1602                    !NotificationData.showNotificationEvenIfUnprovisioned(ent.notification)) {
1603                continue;
1604            }
1605            toShow.add(ent.icon);
1606        }
1607
1608        if (DEBUG) {
1609            Log.d(TAG, "refreshing icons: " + toShow.size() +
1610                    " notifications, mNotificationIcons=" + mNotificationIcons);
1611        }
1612
1613        ArrayList<View> toRemove = new ArrayList<View>();
1614        for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
1615            View child = mNotificationIcons.getChildAt(i);
1616            if (!toShow.contains(child)) {
1617                toRemove.add(child);
1618            }
1619        }
1620
1621        final int toRemoveCount = toRemove.size();
1622        for (int i = 0; i < toRemoveCount; i++) {
1623            mNotificationIcons.removeView(toRemove.get(i));
1624        }
1625
1626        for (int i=0; i<toShow.size(); i++) {
1627            View v = toShow.get(i);
1628            if (v.getParent() == null) {
1629                mNotificationIcons.addView(v, i, params);
1630            }
1631        }
1632
1633        // Resort notification icons
1634        final int childCount = mNotificationIcons.getChildCount();
1635        for (int i = 0; i < childCount; i++) {
1636            View actual = mNotificationIcons.getChildAt(i);
1637            StatusBarIconView expected = toShow.get(i);
1638            if (actual == expected) {
1639                continue;
1640            }
1641            mNotificationIcons.removeView(expected);
1642            mNotificationIcons.addView(expected, i);
1643        }
1644    }
1645
1646    @Override
1647    protected void updateRowStates() {
1648        super.updateRowStates();
1649        mNotificationPanel.notifyVisibleChildrenChanged();
1650    }
1651
1652    protected void updateCarrierLabelVisibility(boolean force) {
1653        // TODO: Handle this for the notification stack scroller as well
1654        if (!mShowCarrierInPanel) return;
1655        // The idea here is to only show the carrier label when there is enough room to see it,
1656        // i.e. when there aren't enough notifications to fill the panel.
1657        if (SPEW) {
1658            Log.d(TAG, String.format("stackScrollerh=%d scrollh=%d carrierh=%d",
1659                    mStackScroller.getHeight(), mStackScroller.getHeight(),
1660                    mCarrierLabelHeight));
1661        }
1662
1663        // Emergency calls only is shown in the expanded header now.
1664        final boolean emergencyCallsShownElsewhere = true;
1665        final boolean makeVisible =
1666            !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
1667            && mStackScroller.getHeight() < (mNotificationPanel.getHeight()
1668                    - mCarrierLabelHeight - mStatusBarHeaderHeight)
1669            && mStackScroller.getVisibility() == View.VISIBLE
1670            && mState != StatusBarState.KEYGUARD;
1671
1672        if (force || mCarrierLabelVisible != makeVisible) {
1673            mCarrierLabelVisible = makeVisible;
1674            if (DEBUG) {
1675                Log.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible"));
1676            }
1677            mCarrierLabel.animate().cancel();
1678            if (makeVisible) {
1679                mCarrierLabel.setVisibility(View.VISIBLE);
1680            }
1681            mCarrierLabel.animate()
1682                .alpha(makeVisible ? 1f : 0f)
1683                //.setStartDelay(makeVisible ? 500 : 0)
1684                //.setDuration(makeVisible ? 750 : 100)
1685                .setDuration(150)
1686                .setListener(makeVisible ? null : new AnimatorListenerAdapter() {
1687                    @Override
1688                    public void onAnimationEnd(Animator animation) {
1689                        if (!mCarrierLabelVisible) { // race
1690                            mCarrierLabel.setVisibility(View.INVISIBLE);
1691                            mCarrierLabel.setAlpha(0f);
1692                        }
1693                    }
1694                })
1695                .start();
1696        }
1697    }
1698
1699    @Override
1700    protected void setAreThereNotifications() {
1701
1702        if (SPEW) {
1703            final boolean clearable = hasActiveNotifications() &&
1704                    mNotificationData.hasActiveClearableNotifications();
1705            Log.d(TAG, "setAreThereNotifications: N=" +
1706                    mNotificationData.getActiveNotifications().size() + " any=" +
1707                    hasActiveNotifications() + " clearable=" + clearable);
1708        }
1709
1710        final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
1711        final boolean showDot = hasActiveNotifications() && !areLightsOn();
1712        if (showDot != (nlo.getAlpha() == 1.0f)) {
1713            if (showDot) {
1714                nlo.setAlpha(0f);
1715                nlo.setVisibility(View.VISIBLE);
1716            }
1717            nlo.animate()
1718                .alpha(showDot?1:0)
1719                .setDuration(showDot?750:250)
1720                .setInterpolator(new AccelerateInterpolator(2.0f))
1721                .setListener(showDot ? null : new AnimatorListenerAdapter() {
1722                    @Override
1723                    public void onAnimationEnd(Animator _a) {
1724                        nlo.setVisibility(View.GONE);
1725                    }
1726                })
1727                .start();
1728        }
1729
1730        findAndUpdateMediaNotifications();
1731
1732        updateCarrierLabelVisibility(false);
1733    }
1734
1735    public void findAndUpdateMediaNotifications() {
1736        boolean metaDataChanged = false;
1737
1738        synchronized (mNotificationData) {
1739            ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
1740            final int N = activeNotifications.size();
1741            Entry mediaNotification = null;
1742            MediaController controller = null;
1743            for (int i = 0; i < N; i++) {
1744                final Entry entry = activeNotifications.get(i);
1745                if (isMediaNotification(entry)) {
1746                    final MediaSession.Token token = entry.notification.getNotification().extras
1747                            .getParcelable(Notification.EXTRA_MEDIA_SESSION);
1748                    if (token != null) {
1749                        controller = new MediaController(mContext, token);
1750                        if (controller != null) {
1751                            // we've got a live one, here
1752                            mediaNotification = entry;
1753                        }
1754                    }
1755                }
1756            }
1757
1758            if (mediaNotification == null) {
1759                // Still nothing? OK, let's just look for live media sessions and see if they match
1760                // one of our notifications. This will catch apps that aren't (yet!) using media
1761                // notifications.
1762
1763                if (mMediaSessionManager != null) {
1764                    final List<MediaController> sessions
1765                            = mMediaSessionManager.getActiveSessionsForUser(
1766                                    null,
1767                                    UserHandle.USER_ALL);
1768
1769                    for (MediaController aController : sessions) {
1770                        if (aController == null) continue;
1771                        final PlaybackState state = aController.getPlaybackState();
1772                        if (state == null) continue;
1773                        switch (state.getState()) {
1774                            case PlaybackState.STATE_STOPPED:
1775                            case PlaybackState.STATE_ERROR:
1776                                continue;
1777                            default:
1778                                // now to see if we have one like this
1779                                final String pkg = aController.getPackageName();
1780
1781                                for (int i = 0; i < N; i++) {
1782                                    final Entry entry = activeNotifications.get(i);
1783                                    if (entry.notification.getPackageName().equals(pkg)) {
1784                                        if (DEBUG_MEDIA) {
1785                                            Log.v(TAG, "DEBUG_MEDIA: found controller matching "
1786                                                + entry.notification.getKey());
1787                                        }
1788                                        controller = aController;
1789                                        mediaNotification = entry;
1790                                        break;
1791                                    }
1792                                }
1793                        }
1794                    }
1795                }
1796            }
1797
1798            if (!sameSessions(mMediaController, controller)) {
1799                // We have a new media session
1800
1801                if (mMediaController != null) {
1802                    // something old was playing
1803                    Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
1804                            + mMediaController);
1805                    mMediaController.unregisterCallback(mMediaListener);
1806                }
1807                mMediaController = controller;
1808
1809                if (mMediaController != null) {
1810                    mMediaController.registerCallback(mMediaListener);
1811                    mMediaMetadata = mMediaController.getMetadata();
1812                    if (DEBUG_MEDIA) {
1813                        Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: "
1814                                + mMediaMetadata);
1815                    }
1816
1817                    final String notificationKey = mediaNotification == null
1818                            ? null
1819                            : mediaNotification.notification.getKey();
1820
1821                    if (notificationKey == null || !notificationKey.equals(mMediaNotificationKey)) {
1822                        // we have a new notification!
1823                        if (DEBUG_MEDIA) {
1824                            Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
1825                                    + notificationKey + " controller=" + controller);
1826                        }
1827                        mMediaNotificationKey = notificationKey;
1828                    }
1829                } else {
1830                    mMediaMetadata = null;
1831                    mMediaNotificationKey = null;
1832                }
1833
1834                metaDataChanged = true;
1835            } else {
1836                // Media session unchanged
1837
1838                if (DEBUG_MEDIA) {
1839                    Log.v(TAG, "DEBUG_MEDIA: Continuing media notification: key=" + mMediaNotificationKey);
1840                }
1841            }
1842        }
1843
1844        updateMediaMetaData(metaDataChanged);
1845    }
1846
1847    private boolean sameSessions(MediaController a, MediaController b) {
1848        if (a == b) return true;
1849        if (a == null) return false;
1850        return a.controlsSameSession(b);
1851    }
1852
1853    /**
1854     * Hide the album artwork that is fading out and release its bitmap.
1855     */
1856    private Runnable mHideBackdropFront = new Runnable() {
1857        @Override
1858        public void run() {
1859            if (DEBUG_MEDIA) {
1860                Log.v(TAG, "DEBUG_MEDIA: removing fade layer");
1861            }
1862            mBackdropFront.setVisibility(View.INVISIBLE);
1863            mBackdropFront.animate().cancel();
1864            mBackdropFront.setImageDrawable(null);
1865        }
1866    };
1867
1868    /**
1869     * Refresh or remove lockscreen artwork from media metadata.
1870     */
1871    public void updateMediaMetaData(boolean metaDataChanged) {
1872        if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) return;
1873
1874        if (mBackdrop == null) return; // called too early
1875
1876        if (DEBUG_MEDIA) {
1877            Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + mMediaNotificationKey
1878                + " metadata=" + mMediaMetadata
1879                + " metaDataChanged=" + metaDataChanged
1880                + " state=" + mState);
1881        }
1882
1883        Bitmap artworkBitmap = null;
1884        if (mMediaMetadata != null) {
1885            artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
1886            if (artworkBitmap == null) {
1887                artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
1888                // might still be null
1889            }
1890        }
1891
1892        final boolean hasArtwork = artworkBitmap != null;
1893
1894        if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
1895                && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
1896            // time to show some art!
1897            if (mBackdrop.getVisibility() != View.VISIBLE) {
1898                mBackdrop.setVisibility(View.VISIBLE);
1899                mBackdrop.animate().alpha(1f);
1900                metaDataChanged = true;
1901                if (DEBUG_MEDIA) {
1902                    Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
1903                }
1904            }
1905            if (metaDataChanged) {
1906                if (mBackdropBack.getDrawable() != null) {
1907                    Drawable drawable = mBackdropBack.getDrawable();
1908                    mBackdropFront.setImageDrawable(drawable);
1909                    if (mScrimSrcModeEnabled) {
1910                        mBackdropFront.getDrawable().mutate().setXfermode(mSrcOverXferMode);
1911                    }
1912                    mBackdropFront.setAlpha(1f);
1913                    mBackdropFront.setVisibility(View.VISIBLE);
1914                } else {
1915                    mBackdropFront.setVisibility(View.INVISIBLE);
1916                }
1917
1918                if (DEBUG_MEDIA_FAKE_ARTWORK) {
1919                    final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF);
1920                    Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c));
1921                    mBackdropBack.setBackgroundColor(0xFFFFFFFF);
1922                    mBackdropBack.setImageDrawable(new ColorDrawable(c));
1923                } else {
1924                    mBackdropBack.setImageBitmap(artworkBitmap);
1925                }
1926                if (mScrimSrcModeEnabled) {
1927                    mBackdropBack.getDrawable().mutate().setXfermode(mSrcXferMode);
1928                }
1929
1930                if (mBackdropFront.getVisibility() == View.VISIBLE) {
1931                    if (DEBUG_MEDIA) {
1932                        Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from "
1933                                + mBackdropFront.getDrawable()
1934                                + " to "
1935                                + mBackdropBack.getDrawable());
1936                    }
1937                    mBackdropFront.animate()
1938                            .setDuration(250)
1939                            .alpha(0f).withEndAction(mHideBackdropFront);
1940                }
1941            }
1942        } else {
1943            // need to hide the album art, either because we are unlocked or because
1944            // the metadata isn't there to support it
1945            if (mBackdrop.getVisibility() != View.GONE) {
1946                if (DEBUG_MEDIA) {
1947                    Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
1948                }
1949                mBackdrop.animate()
1950                        .alpha(0f)
1951                        .setInterpolator(mBackdropInterpolator)
1952                        .setDuration(300)
1953                        .setStartDelay(0)
1954                        .withEndAction(new Runnable() {
1955                            @Override
1956                            public void run() {
1957                                mBackdrop.setVisibility(View.GONE);
1958                                mBackdropFront.animate().cancel();
1959                                mBackdropBack.animate().cancel();
1960                                mHandler.post(mHideBackdropFront);
1961                            }
1962                        });
1963                if (mKeyguardFadingAway) {
1964                    mBackdrop.animate()
1965
1966                            // Make it disappear faster, as the focus should be on the activity behind.
1967                            .setDuration(mKeyguardFadingAwayDuration / 2)
1968                            .setStartDelay(mKeyguardFadingAwayDelay)
1969                            .setInterpolator(mLinearInterpolator)
1970                            .start();
1971                }
1972            }
1973        }
1974    }
1975
1976    public void showClock(boolean show) {
1977        if (mStatusBarView == null) return;
1978        View clock = mStatusBarView.findViewById(R.id.clock);
1979        if (clock != null) {
1980            clock.setVisibility(show ? View.VISIBLE : View.GONE);
1981        }
1982    }
1983
1984    private int adjustDisableFlags(int state) {
1985        if (!mLaunchTransitionFadingAway
1986                && (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit)) {
1987            state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS;
1988            state |= StatusBarManager.DISABLE_SYSTEM_INFO;
1989        }
1990        return state;
1991    }
1992
1993    /**
1994     * State is one or more of the DISABLE constants from StatusBarManager.
1995     */
1996    public void disable(int state, boolean animate) {
1997        mDisabledUnmodified = state;
1998        state = adjustDisableFlags(state);
1999        final int old = mDisabled;
2000        final int diff = state ^ old;
2001        mDisabled = state;
2002
2003        if (DEBUG) {
2004            Log.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)",
2005                old, state, diff));
2006        }
2007
2008        StringBuilder flagdbg = new StringBuilder();
2009        flagdbg.append("disable: < ");
2010        flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand");
2011        flagdbg.append(((diff  & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " ");
2012        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons");
2013        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
2014        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
2015        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
2016        flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
2017        flagdbg.append(((diff  & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
2018        flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
2019        flagdbg.append(((diff  & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
2020        flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home");
2021        flagdbg.append(((diff  & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " ");
2022        flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent");
2023        flagdbg.append(((diff  & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " ");
2024        flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
2025        flagdbg.append(((diff  & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
2026        flagdbg.append(((state & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search");
2027        flagdbg.append(((diff  & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " ");
2028        flagdbg.append(">");
2029        Log.d(TAG, flagdbg.toString());
2030
2031        if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
2032            mSystemIconArea.animate().cancel();
2033            if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
2034                animateStatusBarHide(mSystemIconArea, animate);
2035            } else {
2036                animateStatusBarShow(mSystemIconArea, animate);
2037            }
2038        }
2039
2040        if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
2041            boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
2042            showClock(show);
2043        }
2044        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
2045            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
2046                animateCollapsePanels();
2047            }
2048        }
2049
2050        if ((diff & (StatusBarManager.DISABLE_HOME
2051                        | StatusBarManager.DISABLE_RECENT
2052                        | StatusBarManager.DISABLE_BACK
2053                        | StatusBarManager.DISABLE_SEARCH)) != 0) {
2054            // the nav bar will take care of these
2055            if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state);
2056
2057            if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
2058                // close recents if it's visible
2059                mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
2060                mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
2061            }
2062        }
2063
2064        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
2065            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
2066                if (mTicking) {
2067                    haltTicker();
2068                }
2069                animateStatusBarHide(mNotificationIconArea, animate);
2070            } else {
2071                animateStatusBarShow(mNotificationIconArea, animate);
2072            }
2073        }
2074
2075        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
2076            mDisableNotificationAlerts =
2077                    (state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
2078            mHeadsUpObserver.onChange(true);
2079        }
2080    }
2081
2082    /**
2083     * Animates {@code v}, a view that is part of the status bar, out.
2084     */
2085    private void animateStatusBarHide(final View v, boolean animate) {
2086        v.animate().cancel();
2087        if (!animate) {
2088            v.setAlpha(0f);
2089            v.setVisibility(View.INVISIBLE);
2090            return;
2091        }
2092        v.animate()
2093                .alpha(0f)
2094                .setDuration(160)
2095                .setStartDelay(0)
2096                .setInterpolator(ALPHA_OUT)
2097                .withEndAction(new Runnable() {
2098                    @Override
2099                    public void run() {
2100                        v.setVisibility(View.INVISIBLE);
2101                    }
2102                });
2103    }
2104
2105    /**
2106     * Animates {@code v}, a view that is part of the status bar, in.
2107     */
2108    private void animateStatusBarShow(View v, boolean animate) {
2109        v.animate().cancel();
2110        v.setVisibility(View.VISIBLE);
2111        if (!animate) {
2112            v.setAlpha(1f);
2113            return;
2114        }
2115        v.animate()
2116                .alpha(1f)
2117                .setDuration(320)
2118                .setInterpolator(ALPHA_IN)
2119                .setStartDelay(50)
2120
2121                // We need to clean up any pending end action from animateStatusBarHide if we call
2122                // both hide and show in the same frame before the animation actually gets started.
2123                // cancel() doesn't really remove the end action.
2124                .withEndAction(null);
2125
2126        // Synchronize the motion with the Keyguard fading if necessary.
2127        if (mKeyguardFadingAway) {
2128            v.animate()
2129                    .setDuration(mKeyguardFadingAwayDuration)
2130                    .setInterpolator(mLinearOutSlowIn)
2131                    .setStartDelay(mKeyguardFadingAwayDelay)
2132                    .start();
2133        }
2134    }
2135
2136    @Override
2137    protected BaseStatusBar.H createHandler() {
2138        return new PhoneStatusBar.H();
2139    }
2140
2141    @Override
2142    public void startActivity(Intent intent, boolean dismissShade) {
2143        startActivityDismissingKeyguard(intent, false, dismissShade);
2144    }
2145
2146    public ScrimController getScrimController() {
2147        return mScrimController;
2148    }
2149
2150    public void setQsExpanded(boolean expanded) {
2151        mStatusBarWindowManager.setQsExpanded(expanded);
2152    }
2153
2154    public boolean isGoingToNotificationShade() {
2155        return mLeaveOpenOnKeyguardHide;
2156    }
2157
2158    public boolean isQsExpanded() {
2159        return mNotificationPanel.isQsExpanded();
2160    }
2161
2162    public boolean isScreenOnComingFromTouch() {
2163        return mScreenOnComingFromTouch;
2164    }
2165
2166    public boolean isFalsingThresholdNeeded() {
2167        boolean onKeyguard = getBarState() == StatusBarState.KEYGUARD;
2168        boolean isCurrentlyInsecure = mUnlockMethodCache.isCurrentlyInsecure();
2169        return onKeyguard && (isCurrentlyInsecure || mDozing || mScreenOnComingFromTouch);
2170    }
2171
2172    public boolean isDozing() {
2173        return mDozing;
2174    }
2175
2176    @Override  // NotificationData.Environment
2177    public String getCurrentMediaNotificationKey() {
2178        return mMediaNotificationKey;
2179    }
2180
2181    public boolean isScrimSrcModeEnabled() {
2182        return mScrimSrcModeEnabled;
2183    }
2184
2185    /**
2186     * To be called when there's a state change in StatusBarKeyguardViewManager.
2187     */
2188    public void onKeyguardViewManagerStatesUpdated() {
2189        logStateToEventlog();
2190    }
2191
2192    @Override  // UnlockMethodCache.OnUnlockMethodChangedListener
2193    public void onUnlockMethodStateChanged() {
2194        logStateToEventlog();
2195    }
2196
2197    /**
2198     * All changes to the status bar and notifications funnel through here and are batched.
2199     */
2200    private class H extends BaseStatusBar.H {
2201        public void handleMessage(Message m) {
2202            super.handleMessage(m);
2203            switch (m.what) {
2204                case MSG_OPEN_NOTIFICATION_PANEL:
2205                    animateExpandNotificationsPanel();
2206                    break;
2207                case MSG_OPEN_SETTINGS_PANEL:
2208                    animateExpandSettingsPanel();
2209                    break;
2210                case MSG_CLOSE_PANELS:
2211                    animateCollapsePanels();
2212                    break;
2213                case MSG_SHOW_HEADS_UP:
2214                    setHeadsUpVisibility(true);
2215                    break;
2216                case MSG_DECAY_HEADS_UP:
2217                    mHeadsUpNotificationView.release();
2218                    setHeadsUpVisibility(false);
2219                    break;
2220                case MSG_HIDE_HEADS_UP:
2221                    mHeadsUpNotificationView.release();
2222                    setHeadsUpVisibility(false);
2223                    break;
2224                case MSG_ESCALATE_HEADS_UP:
2225                    escalateHeadsUp();
2226                    setHeadsUpVisibility(false);
2227                    break;
2228                case MSG_LAUNCH_TRANSITION_TIMEOUT:
2229                    onLaunchTransitionTimeout();
2230                    break;
2231            }
2232        }
2233    }
2234
2235    /**  if the interrupting notification had a fullscreen intent, fire it now.  */
2236    private void escalateHeadsUp() {
2237        if (mHeadsUpNotificationView.getEntry() != null) {
2238            final StatusBarNotification sbn = mHeadsUpNotificationView.getEntry().notification;
2239            mHeadsUpNotificationView.release();
2240            final Notification notification = sbn.getNotification();
2241            if (notification.fullScreenIntent != null) {
2242                if (DEBUG)
2243                    Log.d(TAG, "converting a heads up to fullScreen");
2244                try {
2245                    EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION,
2246                            sbn.getKey());
2247                    notification.fullScreenIntent.send();
2248                } catch (PendingIntent.CanceledException e) {
2249                }
2250            }
2251        }
2252    }
2253
2254    View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
2255        public void onFocusChange(View v, boolean hasFocus) {
2256            // Because 'v' is a ViewGroup, all its children will be (un)selected
2257            // too, which allows marqueeing to work.
2258            v.setSelected(hasFocus);
2259        }
2260    };
2261
2262    boolean panelsEnabled() {
2263        return (mDisabled & StatusBarManager.DISABLE_EXPAND) == 0;
2264    }
2265
2266    void makeExpandedVisible(boolean force) {
2267        if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
2268        if (!force && (mExpandedVisible || !panelsEnabled())) {
2269            return;
2270        }
2271
2272        mExpandedVisible = true;
2273        if (mNavigationBarView != null)
2274            mNavigationBarView.setSlippery(true);
2275
2276        updateCarrierLabelVisibility(true);
2277
2278        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
2279
2280        // Expand the window to encompass the full screen in anticipation of the drag.
2281        // This is only possible to do atomically because the status bar is at the top of the screen!
2282        mStatusBarWindowManager.setStatusBarExpanded(true);
2283        mStatusBarView.setFocusable(false);
2284
2285        visibilityChanged(true);
2286        mWaitingForKeyguardExit = false;
2287        disable(mDisabledUnmodified, !force /* animate */);
2288        setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
2289    }
2290
2291    public void animateCollapsePanels() {
2292        animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
2293    }
2294
2295    private final Runnable mAnimateCollapsePanels = new Runnable() {
2296        @Override
2297        public void run() {
2298            animateCollapsePanels();
2299        }
2300    };
2301
2302    public void postAnimateCollapsePanels() {
2303        mHandler.post(mAnimateCollapsePanels);
2304    }
2305
2306    public void animateCollapsePanels(int flags) {
2307        animateCollapsePanels(flags, false /* force */);
2308    }
2309
2310    public void animateCollapsePanels(int flags, boolean force) {
2311        if (!force &&
2312                (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
2313            runPostCollapseRunnables();
2314            return;
2315        }
2316        if (SPEW) {
2317            Log.d(TAG, "animateCollapse():"
2318                    + " mExpandedVisible=" + mExpandedVisible
2319                    + " flags=" + flags);
2320        }
2321
2322        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
2323            if (!mHandler.hasMessages(MSG_HIDE_RECENT_APPS)) {
2324                mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
2325                mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
2326            }
2327        }
2328
2329        if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
2330            mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
2331            mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
2332        }
2333
2334        if (mStatusBarWindow != null) {
2335            // release focus immediately to kick off focus change transition
2336            mStatusBarWindowManager.setStatusBarFocusable(false);
2337
2338            mStatusBarWindow.cancelExpandHelper();
2339            mStatusBarView.collapseAllPanels(true);
2340        }
2341    }
2342
2343    private void runPostCollapseRunnables() {
2344        int size = mPostCollapseRunnables.size();
2345        for (int i = 0; i < size; i++) {
2346            mPostCollapseRunnables.get(i).run();
2347        }
2348        mPostCollapseRunnables.clear();
2349    }
2350
2351    public ViewPropertyAnimator setVisibilityWhenDone(
2352            final ViewPropertyAnimator a, final View v, final int vis) {
2353        a.setListener(new AnimatorListenerAdapter() {
2354            @Override
2355            public void onAnimationEnd(Animator animation) {
2356                v.setVisibility(vis);
2357                a.setListener(null); // oneshot
2358            }
2359        });
2360        return a;
2361    }
2362
2363    public Animator setVisibilityWhenDone(
2364            final Animator a, final View v, final int vis) {
2365        a.addListener(new AnimatorListenerAdapter() {
2366            @Override
2367            public void onAnimationEnd(Animator animation) {
2368                v.setVisibility(vis);
2369            }
2370        });
2371        return a;
2372    }
2373
2374    public Animator interpolator(TimeInterpolator ti, Animator a) {
2375        a.setInterpolator(ti);
2376        return a;
2377    }
2378
2379    public Animator startDelay(int d, Animator a) {
2380        a.setStartDelay(d);
2381        return a;
2382    }
2383
2384    public Animator start(Animator a) {
2385        a.start();
2386        return a;
2387    }
2388
2389    final TimeInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
2390    final TimeInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
2391    final int FLIP_DURATION_OUT = 125;
2392    final int FLIP_DURATION_IN = 225;
2393    final int FLIP_DURATION = (FLIP_DURATION_IN + FLIP_DURATION_OUT);
2394
2395    Animator mScrollViewAnim, mClearButtonAnim;
2396
2397    @Override
2398    public void animateExpandNotificationsPanel() {
2399        if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
2400        if (!panelsEnabled()) {
2401            return ;
2402        }
2403
2404        mNotificationPanel.expand();
2405
2406        if (false) postStartTracing();
2407    }
2408
2409    @Override
2410    public void animateExpandSettingsPanel() {
2411        if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
2412        if (!panelsEnabled()) {
2413            return;
2414        }
2415
2416        // Settings are not available in setup
2417        if (!mUserSetup) return;
2418
2419        mNotificationPanel.expand();
2420        mNotificationPanel.openQs();
2421
2422        if (false) postStartTracing();
2423    }
2424
2425    public void animateCollapseQuickSettings() {
2426        if (mState == StatusBarState.SHADE) {
2427            mStatusBarView.collapseAllPanels(true);
2428        }
2429    }
2430
2431    void makeExpandedInvisible() {
2432        if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
2433                + " mExpandedVisible=" + mExpandedVisible);
2434
2435        if (!mExpandedVisible || mStatusBarWindow == null) {
2436            return;
2437        }
2438
2439        // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
2440        mStatusBarView.collapseAllPanels(/*animate=*/ false);
2441
2442        // reset things to their proper state
2443        if (mScrollViewAnim != null) mScrollViewAnim.cancel();
2444        if (mClearButtonAnim != null) mClearButtonAnim.cancel();
2445
2446        mStackScroller.setVisibility(View.VISIBLE);
2447        mNotificationPanel.setVisibility(View.GONE);
2448
2449        mNotificationPanel.closeQs();
2450
2451        mExpandedVisible = false;
2452        if (mNavigationBarView != null)
2453            mNavigationBarView.setSlippery(false);
2454        visibilityChanged(false);
2455
2456        // Shrink the window to the size of the status bar only
2457        mStatusBarWindowManager.setStatusBarExpanded(false);
2458        mStatusBarView.setFocusable(true);
2459
2460        // Close any "App info" popups that might have snuck on-screen
2461        dismissPopups();
2462
2463        runPostCollapseRunnables();
2464        setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
2465        showBouncer();
2466        disable(mDisabledUnmodified, true /* animate */);
2467
2468        // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
2469        // the bouncer appear animation.
2470        if (!mStatusBarKeyguardViewManager.isShowing()) {
2471            WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
2472        }
2473    }
2474
2475    public boolean interceptTouchEvent(MotionEvent event) {
2476        if (DEBUG_GESTURES) {
2477            if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
2478                EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
2479                        event.getActionMasked(), (int) event.getX(), (int) event.getY(), mDisabled);
2480            }
2481
2482        }
2483
2484        if (SPEW) {
2485            Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
2486                + mDisabled + " mTracking=" + mTracking);
2487        } else if (CHATTY) {
2488            if (event.getAction() != MotionEvent.ACTION_MOVE) {
2489                Log.d(TAG, String.format(
2490                            "panel: %s at (%f, %f) mDisabled=0x%08x",
2491                            MotionEvent.actionToString(event.getAction()),
2492                            event.getRawX(), event.getRawY(), mDisabled));
2493            }
2494        }
2495
2496        if (DEBUG_GESTURES) {
2497            mGestureRec.add(event);
2498        }
2499
2500        if (mStatusBarWindowState == WINDOW_STATE_SHOWING) {
2501            final boolean upOrCancel =
2502                    event.getAction() == MotionEvent.ACTION_UP ||
2503                    event.getAction() == MotionEvent.ACTION_CANCEL;
2504            if (upOrCancel && !mExpandedVisible) {
2505                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
2506            } else {
2507                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
2508            }
2509        }
2510        return false;
2511    }
2512
2513    public GestureRecorder getGestureRecorder() {
2514        return mGestureRec;
2515    }
2516
2517    private void setNavigationIconHints(int hints) {
2518        if (hints == mNavigationIconHints) return;
2519
2520        mNavigationIconHints = hints;
2521
2522        if (mNavigationBarView != null) {
2523            mNavigationBarView.setNavigationIconHints(hints);
2524        }
2525        checkBarModes();
2526    }
2527
2528    @Override // CommandQueue
2529    public void setWindowState(int window, int state) {
2530        boolean showing = state == WINDOW_STATE_SHOWING;
2531        if (mStatusBarWindow != null
2532                && window == StatusBarManager.WINDOW_STATUS_BAR
2533                && mStatusBarWindowState != state) {
2534            mStatusBarWindowState = state;
2535            if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
2536            if (!showing && mState == StatusBarState.SHADE) {
2537                mStatusBarView.collapseAllPanels(false);
2538            }
2539        }
2540        if (mNavigationBarView != null
2541                && window == StatusBarManager.WINDOW_NAVIGATION_BAR
2542                && mNavigationBarWindowState != state) {
2543            mNavigationBarWindowState = state;
2544            if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
2545        }
2546    }
2547
2548    @Override // CommandQueue
2549    public void buzzBeepBlinked() {
2550        if (mDozeServiceHost != null) {
2551            mDozeServiceHost.fireBuzzBeepBlinked();
2552        }
2553    }
2554
2555    @Override
2556    public void notificationLightOff() {
2557        if (mDozeServiceHost != null) {
2558            mDozeServiceHost.fireNotificationLight(false);
2559        }
2560    }
2561
2562    @Override
2563    public void notificationLightPulse(int argb, int onMillis, int offMillis) {
2564        if (mDozeServiceHost != null) {
2565            mDozeServiceHost.fireNotificationLight(true);
2566        }
2567    }
2568
2569    @Override // CommandQueue
2570    public void setSystemUiVisibility(int vis, int mask) {
2571        final int oldVal = mSystemUiVisibility;
2572        final int newVal = (oldVal&~mask) | (vis&mask);
2573        final int diff = newVal ^ oldVal;
2574        if (DEBUG) Log.d(TAG, String.format(
2575                "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
2576                Integer.toHexString(vis), Integer.toHexString(mask),
2577                Integer.toHexString(oldVal), Integer.toHexString(newVal),
2578                Integer.toHexString(diff)));
2579        if (diff != 0) {
2580            // we never set the recents bit via this method, so save the prior state to prevent
2581            // clobbering the bit below
2582            final boolean wasRecentsVisible = (mSystemUiVisibility & View.RECENT_APPS_VISIBLE) > 0;
2583
2584            mSystemUiVisibility = newVal;
2585
2586            // update low profile
2587            if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
2588                final boolean lightsOut = (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0;
2589                if (lightsOut) {
2590                    animateCollapsePanels();
2591                    if (mTicking) {
2592                        haltTicker();
2593                    }
2594                }
2595
2596                setAreThereNotifications();
2597            }
2598
2599            // update status bar mode
2600            final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(),
2601                    View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT);
2602
2603            // update navigation bar mode
2604            final int nbMode = mNavigationBarView == null ? -1 : computeBarMode(
2605                    oldVal, newVal, mNavigationBarView.getBarTransitions(),
2606                    View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT);
2607            final boolean sbModeChanged = sbMode != -1;
2608            final boolean nbModeChanged = nbMode != -1;
2609            boolean checkBarModes = false;
2610            if (sbModeChanged && sbMode != mStatusBarMode) {
2611                mStatusBarMode = sbMode;
2612                checkBarModes = true;
2613            }
2614            if (nbModeChanged && nbMode != mNavigationBarMode) {
2615                mNavigationBarMode = nbMode;
2616                checkBarModes = true;
2617            }
2618            if (checkBarModes) {
2619                checkBarModes();
2620            }
2621            if (sbModeChanged || nbModeChanged) {
2622                // update transient bar autohide
2623                if (mStatusBarMode == MODE_SEMI_TRANSPARENT || mNavigationBarMode == MODE_SEMI_TRANSPARENT) {
2624                    scheduleAutohide();
2625                } else {
2626                    cancelAutohide();
2627                }
2628            }
2629
2630            // ready to unhide
2631            if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
2632                mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
2633            }
2634            if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
2635                mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
2636            }
2637
2638            // restore the recents bit
2639            if (wasRecentsVisible) {
2640                mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
2641            }
2642
2643            // send updated sysui visibility to window manager
2644            notifyUiVisibilityChanged(mSystemUiVisibility);
2645        }
2646    }
2647
2648    private int computeBarMode(int oldVis, int newVis, BarTransitions transitions,
2649            int transientFlag, int translucentFlag) {
2650        final int oldMode = barMode(oldVis, transientFlag, translucentFlag);
2651        final int newMode = barMode(newVis, transientFlag, translucentFlag);
2652        if (oldMode == newMode) {
2653            return -1; // no mode change
2654        }
2655        return newMode;
2656    }
2657
2658    private int barMode(int vis, int transientFlag, int translucentFlag) {
2659        int lightsOutTransparent = View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_TRANSPARENT;
2660        return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT
2661                : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT
2662                : (vis & lightsOutTransparent) == lightsOutTransparent ? MODE_LIGHTS_OUT_TRANSPARENT
2663                : (vis & View.SYSTEM_UI_TRANSPARENT) != 0 ? MODE_TRANSPARENT
2664                : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT
2665                : MODE_OPAQUE;
2666    }
2667
2668    private void checkBarModes() {
2669        if (mDemoMode) return;
2670        checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions());
2671        if (mNavigationBarView != null) {
2672            checkBarMode(mNavigationBarMode,
2673                    mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
2674        }
2675    }
2676
2677    private void checkBarMode(int mode, int windowState, BarTransitions transitions) {
2678        final boolean powerSave = mBatteryController.isPowerSave();
2679        final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN
2680                && !powerSave;
2681        if (powerSave && getBarState() == StatusBarState.SHADE) {
2682            mode = MODE_WARNING;
2683        }
2684        transitions.transitionTo(mode, anim);
2685    }
2686
2687    private void finishBarAnimations() {
2688        mStatusBarView.getBarTransitions().finishAnimations();
2689        if (mNavigationBarView != null) {
2690            mNavigationBarView.getBarTransitions().finishAnimations();
2691        }
2692    }
2693
2694    private final Runnable mCheckBarModes = new Runnable() {
2695        @Override
2696        public void run() {
2697            checkBarModes();
2698        }
2699    };
2700
2701    @Override
2702    public void setInteracting(int barWindow, boolean interacting) {
2703        final boolean changing = ((mInteractingWindows & barWindow) != 0) != interacting;
2704        mInteractingWindows = interacting
2705                ? (mInteractingWindows | barWindow)
2706                : (mInteractingWindows & ~barWindow);
2707        if (mInteractingWindows != 0) {
2708            suspendAutohide();
2709        } else {
2710            resumeSuspendedAutohide();
2711        }
2712        // manually dismiss the volume panel when interacting with the nav bar
2713        if (changing && interacting && barWindow == StatusBarManager.WINDOW_NAVIGATION_BAR) {
2714            if (mVolumeComponent != null) {
2715                mVolumeComponent.dismissNow();
2716            }
2717        }
2718        checkBarModes();
2719    }
2720
2721    private void resumeSuspendedAutohide() {
2722        if (mAutohideSuspended) {
2723            scheduleAutohide();
2724            mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher
2725        }
2726    }
2727
2728    private void suspendAutohide() {
2729        mHandler.removeCallbacks(mAutohide);
2730        mHandler.removeCallbacks(mCheckBarModes);
2731        mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0;
2732    }
2733
2734    private void cancelAutohide() {
2735        mAutohideSuspended = false;
2736        mHandler.removeCallbacks(mAutohide);
2737    }
2738
2739    private void scheduleAutohide() {
2740        cancelAutohide();
2741        mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS);
2742    }
2743
2744    private void checkUserAutohide(View v, MotionEvent event) {
2745        if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0  // a transient bar is revealed
2746                && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
2747                && event.getX() == 0 && event.getY() == 0  // a touch outside both bars
2748                ) {
2749            userAutohide();
2750        }
2751    }
2752
2753    private void userAutohide() {
2754        cancelAutohide();
2755        mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear
2756    }
2757
2758    private boolean areLightsOn() {
2759        return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
2760    }
2761
2762    public void setLightsOn(boolean on) {
2763        Log.v(TAG, "setLightsOn(" + on + ")");
2764        if (on) {
2765            setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
2766        } else {
2767            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
2768        }
2769    }
2770
2771    private void notifyUiVisibilityChanged(int vis) {
2772        try {
2773            mWindowManagerService.statusBarVisibilityChanged(vis);
2774        } catch (RemoteException ex) {
2775        }
2776    }
2777
2778    public void topAppWindowChanged(boolean showMenu) {
2779        if (DEBUG) {
2780            Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
2781        }
2782        if (mNavigationBarView != null) {
2783            mNavigationBarView.setMenuVisibility(showMenu);
2784        }
2785
2786        // See above re: lights-out policy for legacy apps.
2787        if (showMenu) setLightsOn(true);
2788    }
2789
2790    @Override
2791    public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
2792            boolean showImeSwitcher) {
2793        boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
2794        int flags = mNavigationIconHints;
2795        if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) {
2796            flags |= NAVIGATION_HINT_BACK_ALT;
2797        } else {
2798            flags &= ~NAVIGATION_HINT_BACK_ALT;
2799        }
2800        if (showImeSwitcher) {
2801            flags |= NAVIGATION_HINT_IME_SHOWN;
2802        } else {
2803            flags &= ~NAVIGATION_HINT_IME_SHOWN;
2804        }
2805
2806        setNavigationIconHints(flags);
2807    }
2808
2809    @Override
2810    protected void tick(StatusBarNotification n, boolean firstTime) {
2811        if (!mTickerEnabled) return;
2812
2813        // no ticking in lights-out mode
2814        if (!areLightsOn()) return;
2815
2816        // no ticking in Setup
2817        if (!isDeviceProvisioned()) return;
2818
2819        // not for you
2820        if (!isNotificationForCurrentProfiles(n)) return;
2821
2822        // Show the ticker if one is requested. Also don't do this
2823        // until status bar window is attached to the window manager,
2824        // because...  well, what's the point otherwise?  And trying to
2825        // run a ticker without being attached will crash!
2826        if (n.getNotification().tickerText != null && mStatusBarWindow != null
2827                && mStatusBarWindow.getWindowToken() != null) {
2828            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
2829                    | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
2830                mTicker.addEntry(n);
2831            }
2832        }
2833    }
2834
2835    private class MyTicker extends Ticker {
2836        MyTicker(Context context, View sb) {
2837            super(context, sb);
2838            if (!mTickerEnabled) {
2839                Log.w(TAG, "MyTicker instantiated with mTickerEnabled=false", new Throwable());
2840            }
2841        }
2842
2843        @Override
2844        public void tickerStarting() {
2845            if (!mTickerEnabled) return;
2846            mTicking = true;
2847            mStatusBarContents.setVisibility(View.GONE);
2848            mTickerView.setVisibility(View.VISIBLE);
2849            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
2850            mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
2851        }
2852
2853        @Override
2854        public void tickerDone() {
2855            if (!mTickerEnabled) return;
2856            mStatusBarContents.setVisibility(View.VISIBLE);
2857            mTickerView.setVisibility(View.GONE);
2858            mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
2859            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
2860                        mTickingDoneListener));
2861        }
2862
2863        public void tickerHalting() {
2864            if (!mTickerEnabled) return;
2865            if (mStatusBarContents.getVisibility() != View.VISIBLE) {
2866                mStatusBarContents.setVisibility(View.VISIBLE);
2867                mStatusBarContents
2868                        .startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
2869            }
2870            mTickerView.setVisibility(View.GONE);
2871            // we do not animate the ticker away at this point, just get rid of it (b/6992707)
2872        }
2873    }
2874
2875    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
2876        public void onAnimationEnd(Animation animation) {
2877            mTicking = false;
2878        }
2879        public void onAnimationRepeat(Animation animation) {
2880        }
2881        public void onAnimationStart(Animation animation) {
2882        }
2883    };
2884
2885    private Animation loadAnim(int id, Animation.AnimationListener listener) {
2886        Animation anim = AnimationUtils.loadAnimation(mContext, id);
2887        if (listener != null) {
2888            anim.setAnimationListener(listener);
2889        }
2890        return anim;
2891    }
2892
2893    public static String viewInfo(View v) {
2894        return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
2895                + ") " + v.getWidth() + "x" + v.getHeight() + "]";
2896    }
2897
2898    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2899        synchronized (mQueueLock) {
2900            pw.println("Current Status Bar state:");
2901            pw.println("  mExpandedVisible=" + mExpandedVisible
2902                    + ", mTrackingPosition=" + mTrackingPosition);
2903            pw.println("  mTickerEnabled=" + mTickerEnabled);
2904            if (mTickerEnabled) {
2905                pw.println("  mTicking=" + mTicking);
2906                pw.println("  mTickerView: " + viewInfo(mTickerView));
2907            }
2908            pw.println("  mTracking=" + mTracking);
2909            pw.println("  mDisplayMetrics=" + mDisplayMetrics);
2910            pw.println("  mStackScroller: " + viewInfo(mStackScroller));
2911            pw.println("  mStackScroller: " + viewInfo(mStackScroller)
2912                    + " scroll " + mStackScroller.getScrollX()
2913                    + "," + mStackScroller.getScrollY());
2914        }
2915
2916        pw.print("  mInteractingWindows="); pw.println(mInteractingWindows);
2917        pw.print("  mStatusBarWindowState=");
2918        pw.println(windowStateToString(mStatusBarWindowState));
2919        pw.print("  mStatusBarMode=");
2920        pw.println(BarTransitions.modeToString(mStatusBarMode));
2921        pw.print("  mDozing="); pw.println(mDozing);
2922        pw.print("  mZenMode=");
2923        pw.println(Settings.Global.zenModeToString(mZenMode));
2924        pw.print("  mUseHeadsUp=");
2925        pw.println(mUseHeadsUp);
2926        pw.print("  interrupting package: ");
2927        pw.println(hunStateToString(mHeadsUpNotificationView.getEntry()));
2928        dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
2929        if (mNavigationBarView != null) {
2930            pw.print("  mNavigationBarWindowState=");
2931            pw.println(windowStateToString(mNavigationBarWindowState));
2932            pw.print("  mNavigationBarMode=");
2933            pw.println(BarTransitions.modeToString(mNavigationBarMode));
2934            dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
2935        }
2936
2937        pw.print("  mNavigationBarView=");
2938        if (mNavigationBarView == null) {
2939            pw.println("null");
2940        } else {
2941            mNavigationBarView.dump(fd, pw, args);
2942        }
2943
2944        pw.print("  mMediaSessionManager=");
2945        pw.println(mMediaSessionManager);
2946        pw.print("  mMediaNotificationKey=");
2947        pw.println(mMediaNotificationKey);
2948        pw.print("  mMediaController=");
2949        pw.print(mMediaController);
2950        if (mMediaController != null) {
2951            pw.print(" state=" + mMediaController.getPlaybackState());
2952        }
2953        pw.println();
2954        pw.print("  mMediaMetadata=");
2955        pw.print(mMediaMetadata);
2956        if (mMediaMetadata != null) {
2957            pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE));
2958        }
2959        pw.println();
2960
2961        pw.println("  Panels: ");
2962        if (mNotificationPanel != null) {
2963            pw.println("    mNotificationPanel=" +
2964                mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""));
2965            pw.print  ("      ");
2966            mNotificationPanel.dump(fd, pw, args);
2967        }
2968
2969        DozeLog.dump(pw);
2970
2971        if (DUMPTRUCK) {
2972            synchronized (mNotificationData) {
2973                mNotificationData.dump(pw, "  ");
2974            }
2975
2976            int N = mStatusIcons.getChildCount();
2977            pw.println("  system icons: " + N);
2978            for (int i=0; i<N; i++) {
2979                StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
2980                pw.println("    [" + i + "] icon=" + ic);
2981            }
2982
2983            if (false) {
2984                pw.println("see the logcat for a dump of the views we have created.");
2985                // must happen on ui thread
2986                mHandler.post(new Runnable() {
2987                        public void run() {
2988                            mStatusBarView.getLocationOnScreen(mAbsPos);
2989                            Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
2990                                    + ") " + mStatusBarView.getWidth() + "x"
2991                                    + getStatusBarHeight());
2992                            mStatusBarView.debug();
2993                        }
2994                    });
2995            }
2996        }
2997
2998        if (DEBUG_GESTURES) {
2999            pw.print("  status bar gestures: ");
3000            mGestureRec.dump(fd, pw, args);
3001        }
3002
3003        if (mNetworkController != null) {
3004            mNetworkController.dump(fd, pw, args);
3005        }
3006        if (mBluetoothController != null) {
3007            mBluetoothController.dump(fd, pw, args);
3008        }
3009        if (mCastController != null) {
3010            mCastController.dump(fd, pw, args);
3011        }
3012        if (mUserSwitcherController != null) {
3013            mUserSwitcherController.dump(fd, pw, args);
3014        }
3015        if (mBatteryController != null) {
3016            mBatteryController.dump(fd, pw, args);
3017        }
3018        if (mNextAlarmController != null) {
3019            mNextAlarmController.dump(fd, pw, args);
3020        }
3021        if (mSecurityController != null) {
3022            mSecurityController.dump(fd, pw, args);
3023        }
3024        pw.println("SharedPreferences:");
3025        for (Map.Entry<String, ?> entry : mContext.getSharedPreferences(mContext.getPackageName(),
3026                Context.MODE_PRIVATE).getAll().entrySet()) {
3027            pw.print("  "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue());
3028        }
3029    }
3030
3031    private String hunStateToString(Entry entry) {
3032        if (entry == null) return "null";
3033        if (entry.notification == null) return "corrupt";
3034        return entry.notification.getPackageName();
3035    }
3036
3037    private static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) {
3038        pw.print("  "); pw.print(var); pw.print(".BarTransitions.mMode=");
3039        pw.println(BarTransitions.modeToString(transitions.getMode()));
3040    }
3041
3042    @Override
3043    public void createAndAddWindows() {
3044        addStatusBarWindow();
3045    }
3046
3047    private void addStatusBarWindow() {
3048        makeStatusBarView();
3049        mStatusBarWindowManager = new StatusBarWindowManager(mContext);
3050        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
3051    }
3052
3053    static final float saturate(float a) {
3054        return a < 0f ? 0f : (a > 1f ? 1f : a);
3055    }
3056
3057    @Override
3058    public void updateExpandedViewPos(int thingy) {
3059        if (SPEW) Log.v(TAG, "updateExpandedViewPos");
3060
3061        // on larger devices, the notification panel is propped open a bit
3062        mNotificationPanel.setMinimumHeight(
3063                (int)(mNotificationPanelMinHeightFrac * mCurrentDisplaySize.y));
3064
3065        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
3066        lp.gravity = mNotificationPanelGravity;
3067        mNotificationPanel.setLayoutParams(lp);
3068
3069        updateCarrierLabelVisibility(false);
3070    }
3071
3072    // called by makeStatusbar and also by PhoneStatusBarView
3073    void updateDisplaySize() {
3074        mDisplay.getMetrics(mDisplayMetrics);
3075        mDisplay.getSize(mCurrentDisplaySize);
3076        if (DEBUG_GESTURES) {
3077            mGestureRec.tag("display",
3078                    String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
3079        }
3080    }
3081
3082    float getDisplayDensity() {
3083        return mDisplayMetrics.density;
3084    }
3085
3086    public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
3087            final boolean dismissShade) {
3088        if (onlyProvisioned && !isDeviceProvisioned()) return;
3089
3090        final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
3091                mContext, intent, mCurrentUserId);
3092        final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
3093        dismissKeyguardThenExecute(new OnDismissAction() {
3094            @Override
3095            public boolean onDismiss() {
3096                AsyncTask.execute(new Runnable() {
3097                    public void run() {
3098                        try {
3099                            if (keyguardShowing && !afterKeyguardGone) {
3100                                ActivityManagerNative.getDefault()
3101                                        .keyguardWaitingForActivityDrawn();
3102                            }
3103                            intent.setFlags(
3104                                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
3105                            mContext.startActivityAsUser(
3106                                    intent, new UserHandle(UserHandle.USER_CURRENT));
3107                            overrideActivityPendingAppTransition(
3108                                    keyguardShowing && !afterKeyguardGone);
3109                        } catch (RemoteException e) {
3110                        }
3111                    }
3112                });
3113                if (dismissShade) {
3114                    animateCollapsePanels(
3115                            CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
3116                }
3117                return true;
3118            }
3119        }, afterKeyguardGone);
3120    }
3121
3122    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
3123        public void onReceive(Context context, Intent intent) {
3124            if (DEBUG) Log.v(TAG, "onReceive: " + intent);
3125            String action = intent.getAction();
3126            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
3127                if (isCurrentProfile(getSendingUserId())) {
3128                    int flags = CommandQueue.FLAG_EXCLUDE_NONE;
3129                    String reason = intent.getStringExtra("reason");
3130                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
3131                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
3132                    }
3133                    animateCollapsePanels(flags);
3134                }
3135            }
3136            else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
3137                mScreenOn = false;
3138                notifyNavigationBarScreenOn(false);
3139                notifyHeadsUpScreenOn(false);
3140                finishBarAnimations();
3141                resetUserExpandedStates();
3142            }
3143            else if (Intent.ACTION_SCREEN_ON.equals(action)) {
3144                mScreenOn = true;
3145                notifyNavigationBarScreenOn(true);
3146            }
3147            else if (ACTION_DEMO.equals(action)) {
3148                Bundle bundle = intent.getExtras();
3149                if (bundle != null) {
3150                    String command = bundle.getString("command", "").trim().toLowerCase();
3151                    if (command.length() > 0) {
3152                        try {
3153                            dispatchDemoCommand(command, bundle);
3154                        } catch (Throwable t) {
3155                            Log.w(TAG, "Error running demo command, intent=" + intent, t);
3156                        }
3157                    }
3158                }
3159            } else if ("fake_artwork".equals(action)) {
3160                if (DEBUG_MEDIA_FAKE_ARTWORK) {
3161                    updateMediaMetaData(true);
3162                }
3163            }
3164        }
3165    };
3166
3167    private void resetUserExpandedStates() {
3168        ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
3169        final int notificationCount = activeNotifications.size();
3170        for (int i = 0; i < notificationCount; i++) {
3171            NotificationData.Entry entry = activeNotifications.get(i);
3172            if (entry.row != null) {
3173                entry.row.resetUserExpansion();
3174            }
3175        }
3176    }
3177
3178    @Override
3179    protected void dismissKeyguardThenExecute(final OnDismissAction action,
3180            boolean afterKeyguardGone) {
3181        if (mStatusBarKeyguardViewManager.isShowing()) {
3182            mStatusBarKeyguardViewManager.dismissWithAction(action, afterKeyguardGone);
3183        } else {
3184            action.onDismiss();
3185        }
3186    }
3187
3188    // SystemUIService notifies SystemBars of configuration changes, which then calls down here
3189    @Override
3190    protected void onConfigurationChanged(Configuration newConfig) {
3191        super.onConfigurationChanged(newConfig); // calls refreshLayout
3192
3193        if (DEBUG) {
3194            Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
3195        }
3196        updateDisplaySize(); // populates mDisplayMetrics
3197
3198        updateResources();
3199        updateClockSize();
3200        repositionNavigationBar();
3201        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
3202        updateShowSearchHoldoff();
3203        updateRowStates();
3204        mScreenPinningRequest.onConfigurationChanged();
3205    }
3206
3207    @Override
3208    public void userSwitched(int newUserId) {
3209        super.userSwitched(newUserId);
3210        if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
3211        animateCollapsePanels();
3212        updatePublicMode();
3213        updateNotifications();
3214        resetUserSetupObserver();
3215        setControllerUsers();
3216    }
3217
3218    private void setControllerUsers() {
3219        if (mZenModeController != null) {
3220            mZenModeController.setUserId(mCurrentUserId);
3221        }
3222    }
3223
3224    private void resetUserSetupObserver() {
3225        mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver);
3226        mUserSetupObserver.onChange(false);
3227        mContext.getContentResolver().registerContentObserver(
3228                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true,
3229                mUserSetupObserver,
3230                mCurrentUserId);
3231    }
3232
3233    private void setHeadsUpVisibility(boolean vis) {
3234        if (!ENABLE_HEADS_UP) return;
3235        if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window");
3236        EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_STATUS,
3237                vis ? mHeadsUpNotificationView.getKey() : "",
3238                vis ? 1 : 0);
3239        mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE);
3240    }
3241
3242    public void onHeadsUpDismissed() {
3243        mHeadsUpNotificationView.dismiss();
3244    }
3245
3246    /**
3247     * Reload some of our resources when the configuration changes.
3248     *
3249     * We don't reload everything when the configuration changes -- we probably
3250     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
3251     * meantime, just update the things that we know change.
3252     */
3253    void updateResources() {
3254        // Update the quick setting tiles
3255        if (mQSPanel != null) {
3256            mQSPanel.updateResources();
3257        }
3258
3259        loadDimens();
3260        mLinearOutSlowIn = AnimationUtils.loadInterpolator(
3261                mContext, android.R.interpolator.linear_out_slow_in);
3262
3263        if (mNotificationPanel != null) {
3264            mNotificationPanel.updateResources();
3265        }
3266        if (mHeadsUpNotificationView != null) {
3267            mHeadsUpNotificationView.updateResources();
3268        }
3269        if (mBrightnessMirrorController != null) {
3270            mBrightnessMirrorController.updateResources();
3271        }
3272    }
3273
3274    private void updateClockSize() {
3275        if (mStatusBarView == null) return;
3276        TextView clock = (TextView) mStatusBarView.findViewById(R.id.clock);
3277        if (clock != null) {
3278            FontSizeUtils.updateFontSize(clock, R.dimen.status_bar_clock_size);
3279        }
3280    }
3281    protected void loadDimens() {
3282        final Resources res = mContext.getResources();
3283
3284        mNaturalBarHeight = res.getDimensionPixelSize(
3285                com.android.internal.R.dimen.status_bar_height);
3286
3287        int newIconSize = res.getDimensionPixelSize(
3288            com.android.internal.R.dimen.status_bar_icon_size);
3289        int newIconHPadding = res.getDimensionPixelSize(
3290            R.dimen.status_bar_icon_padding);
3291
3292        if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
3293//            Log.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
3294            mIconHPadding = newIconHPadding;
3295            mIconSize = newIconSize;
3296            //reloadAllNotificationIcons(); // reload the tray
3297        }
3298
3299        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
3300
3301        mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
3302        if (mNotificationPanelGravity <= 0) {
3303            mNotificationPanelGravity = Gravity.START | Gravity.TOP;
3304        }
3305
3306        mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
3307        mStatusBarHeaderHeight = res.getDimensionPixelSize(R.dimen.status_bar_header_height);
3308
3309        mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1);
3310        if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) {
3311            mNotificationPanelMinHeightFrac = 0f;
3312        }
3313
3314        mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay);
3315        mRowMinHeight =  res.getDimensionPixelSize(R.dimen.notification_min_height);
3316        mRowMaxHeight =  res.getDimensionPixelSize(R.dimen.notification_max_height);
3317
3318        mKeyguardMaxNotificationCount = res.getInteger(R.integer.keyguard_max_notification_count);
3319
3320        if (DEBUG) Log.v(TAG, "updateResources");
3321    }
3322
3323    // Visibility reporting
3324
3325    @Override
3326    protected void handleVisibleToUserChanged(boolean visibleToUser) {
3327        if (visibleToUser) {
3328            super.handleVisibleToUserChanged(visibleToUser);
3329            startNotificationLogging();
3330        } else {
3331            stopNotificationLogging();
3332            super.handleVisibleToUserChanged(visibleToUser);
3333        }
3334    }
3335
3336    private void stopNotificationLogging() {
3337        // Report all notifications as invisible and turn down the
3338        // reporter.
3339        if (!mCurrentlyVisibleNotifications.isEmpty()) {
3340            logNotificationVisibilityChanges(
3341                    Collections.<String>emptyList(), mCurrentlyVisibleNotifications);
3342            mCurrentlyVisibleNotifications.clear();
3343        }
3344        mHandler.removeCallbacks(mVisibilityReporter);
3345        mStackScroller.setChildLocationsChangedListener(null);
3346    }
3347
3348    private void startNotificationLogging() {
3349        mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
3350        // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't
3351        // cause the scroller to emit child location events. Hence generate
3352        // one ourselves to guarantee that we're reporting visible
3353        // notifications.
3354        // (Note that in cases where the scroller does emit events, this
3355        // additional event doesn't break anything.)
3356        mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller);
3357    }
3358
3359    private void logNotificationVisibilityChanges(
3360            Collection<String> newlyVisible, Collection<String> noLongerVisible) {
3361        if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
3362            return;
3363        }
3364        String[] newlyVisibleAr = newlyVisible.toArray(new String[newlyVisible.size()]);
3365        String[] noLongerVisibleAr = noLongerVisible.toArray(new String[noLongerVisible.size()]);
3366        try {
3367            mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
3368        } catch (RemoteException e) {
3369            // Ignore.
3370        }
3371    }
3372
3373    // State logging
3374
3375    private void logStateToEventlog() {
3376        boolean isShowing = mStatusBarKeyguardViewManager.isShowing();
3377        boolean isOccluded = mStatusBarKeyguardViewManager.isOccluded();
3378        boolean isBouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing();
3379        boolean isSecure = mUnlockMethodCache.isMethodSecure();
3380        boolean isCurrentlyInsecure = mUnlockMethodCache.isCurrentlyInsecure();
3381        int stateFingerprint = getLoggingFingerprint(mState,
3382                isShowing,
3383                isOccluded,
3384                isBouncerShowing,
3385                isSecure,
3386                isCurrentlyInsecure);
3387        if (stateFingerprint != mLastLoggedStateFingerprint) {
3388            EventLogTags.writeSysuiStatusBarState(mState,
3389                    isShowing ? 1 : 0,
3390                    isOccluded ? 1 : 0,
3391                    isBouncerShowing ? 1 : 0,
3392                    isSecure ? 1 : 0,
3393                    isCurrentlyInsecure ? 1 : 0);
3394            mLastLoggedStateFingerprint = stateFingerprint;
3395        }
3396    }
3397
3398    /**
3399     * Returns a fingerprint of fields logged to eventlog
3400     */
3401    private static int getLoggingFingerprint(int statusBarState, boolean keyguardShowing,
3402            boolean keyguardOccluded, boolean bouncerShowing, boolean secure,
3403            boolean currentlyInsecure) {
3404        // Reserve 8 bits for statusBarState. We'll never go higher than
3405        // that, right? Riiiight.
3406        return (statusBarState & 0xFF)
3407                | ((keyguardShowing   ? 1 : 0) <<  8)
3408                | ((keyguardOccluded  ? 1 : 0) <<  9)
3409                | ((bouncerShowing    ? 1 : 0) << 10)
3410                | ((secure            ? 1 : 0) << 11)
3411                | ((currentlyInsecure ? 1 : 0) << 12);
3412    }
3413
3414    //
3415    // tracing
3416    //
3417
3418    void postStartTracing() {
3419        mHandler.postDelayed(mStartTracing, 3000);
3420    }
3421
3422    void vibrate() {
3423        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
3424                Context.VIBRATOR_SERVICE);
3425        vib.vibrate(250, VIBRATION_ATTRIBUTES);
3426    }
3427
3428    Runnable mStartTracing = new Runnable() {
3429        public void run() {
3430            vibrate();
3431            SystemClock.sleep(250);
3432            Log.d(TAG, "startTracing");
3433            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
3434            mHandler.postDelayed(mStopTracing, 10000);
3435        }
3436    };
3437
3438    Runnable mStopTracing = new Runnable() {
3439        public void run() {
3440            android.os.Debug.stopMethodTracing();
3441            Log.d(TAG, "stopTracing");
3442            vibrate();
3443        }
3444    };
3445
3446    @Override
3447    protected void haltTicker() {
3448        if (mTickerEnabled) {
3449            mTicker.halt();
3450        }
3451    }
3452
3453    @Override
3454    protected boolean shouldDisableNavbarGestures() {
3455        return !isDeviceProvisioned()
3456                || mExpandedVisible
3457                || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0;
3458    }
3459
3460    public void postStartSettingsActivity(final Intent intent, int delay) {
3461        mHandler.postDelayed(new Runnable() {
3462            @Override
3463            public void run() {
3464                handleStartSettingsActivity(intent, true /*onlyProvisioned*/);
3465            }
3466        }, delay);
3467    }
3468
3469    private void handleStartSettingsActivity(Intent intent, boolean onlyProvisioned) {
3470        startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */);
3471    }
3472
3473    private static class FastColorDrawable extends Drawable {
3474        private final int mColor;
3475
3476        public FastColorDrawable(int color) {
3477            mColor = 0xff000000 | color;
3478        }
3479
3480        @Override
3481        public void draw(Canvas canvas) {
3482            canvas.drawColor(mColor, PorterDuff.Mode.SRC);
3483        }
3484
3485        @Override
3486        public void setAlpha(int alpha) {
3487        }
3488
3489        @Override
3490        public void setColorFilter(ColorFilter cf) {
3491        }
3492
3493        @Override
3494        public int getOpacity() {
3495            return PixelFormat.OPAQUE;
3496        }
3497
3498        @Override
3499        public void setBounds(int left, int top, int right, int bottom) {
3500        }
3501
3502        @Override
3503        public void setBounds(Rect bounds) {
3504        }
3505    }
3506
3507    @Override
3508    public void destroy() {
3509        super.destroy();
3510        if (mStatusBarWindow != null) {
3511            mWindowManager.removeViewImmediate(mStatusBarWindow);
3512            mStatusBarWindow = null;
3513        }
3514        if (mNavigationBarView != null) {
3515            mWindowManager.removeViewImmediate(mNavigationBarView);
3516            mNavigationBarView = null;
3517        }
3518        if (mHandlerThread != null) {
3519            mHandlerThread.quitSafely();
3520            mHandlerThread = null;
3521        }
3522        mContext.unregisterReceiver(mBroadcastReceiver);
3523    }
3524
3525    private boolean mDemoModeAllowed;
3526    private boolean mDemoMode;
3527    private DemoStatusIcons mDemoStatusIcons;
3528
3529    @Override
3530    public void dispatchDemoCommand(String command, Bundle args) {
3531        if (!mDemoModeAllowed) {
3532            mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(),
3533                    "sysui_demo_allowed", 0) != 0;
3534        }
3535        if (!mDemoModeAllowed) return;
3536        if (command.equals(COMMAND_ENTER)) {
3537            mDemoMode = true;
3538        } else if (command.equals(COMMAND_EXIT)) {
3539            mDemoMode = false;
3540            checkBarModes();
3541        } else if (!mDemoMode) {
3542            // automatically enter demo mode on first demo command
3543            dispatchDemoCommand(COMMAND_ENTER, new Bundle());
3544        }
3545        boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT);
3546        if ((modeChange || command.equals(COMMAND_VOLUME)) && mVolumeComponent != null) {
3547            mVolumeComponent.dispatchDemoCommand(command, args);
3548        }
3549        if (modeChange || command.equals(COMMAND_CLOCK)) {
3550            dispatchDemoCommandToView(command, args, R.id.clock);
3551        }
3552        if (modeChange || command.equals(COMMAND_BATTERY)) {
3553            dispatchDemoCommandToView(command, args, R.id.battery);
3554        }
3555        if (modeChange || command.equals(COMMAND_STATUS)) {
3556            if (mDemoStatusIcons == null) {
3557                mDemoStatusIcons = new DemoStatusIcons(mStatusIcons, mIconSize);
3558            }
3559            mDemoStatusIcons.dispatchDemoCommand(command, args);
3560        }
3561        if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) {
3562            mNetworkController.dispatchDemoCommand(command, args);
3563        }
3564        if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) {
3565            View notifications = mStatusBarView == null ? null
3566                    : mStatusBarView.findViewById(R.id.notification_icon_area);
3567            if (notifications != null) {
3568                String visible = args.getString("visible");
3569                int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE;
3570                notifications.setVisibility(vis);
3571            }
3572        }
3573        if (command.equals(COMMAND_BARS)) {
3574            String mode = args.getString("mode");
3575            int barMode = "opaque".equals(mode) ? MODE_OPAQUE :
3576                    "translucent".equals(mode) ? MODE_TRANSLUCENT :
3577                    "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT :
3578                    "transparent".equals(mode) ? MODE_TRANSPARENT :
3579                    "warning".equals(mode) ? MODE_WARNING :
3580                    -1;
3581            if (barMode != -1) {
3582                boolean animate = true;
3583                if (mStatusBarView != null) {
3584                    mStatusBarView.getBarTransitions().transitionTo(barMode, animate);
3585                }
3586                if (mNavigationBarView != null) {
3587                    mNavigationBarView.getBarTransitions().transitionTo(barMode, animate);
3588                }
3589            }
3590        }
3591    }
3592
3593    private void dispatchDemoCommandToView(String command, Bundle args, int id) {
3594        if (mStatusBarView == null) return;
3595        View v = mStatusBarView.findViewById(id);
3596        if (v instanceof DemoMode) {
3597            ((DemoMode)v).dispatchDemoCommand(command, args);
3598        }
3599    }
3600
3601    /**
3602     * @return The {@link StatusBarState} the status bar is in.
3603     */
3604    public int getBarState() {
3605        return mState;
3606    }
3607
3608    public void showKeyguard() {
3609        if (mLaunchTransitionFadingAway) {
3610            mNotificationPanel.animate().cancel();
3611            mNotificationPanel.setAlpha(1f);
3612            runLaunchTransitionEndRunnable();
3613            mLaunchTransitionFadingAway = false;
3614        }
3615        mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
3616        setBarState(StatusBarState.KEYGUARD);
3617        updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
3618        if (!mScreenOnFromKeyguard) {
3619
3620            // If the screen is off already, we need to disable touch events because these might
3621            // collapse the panel after we expanded it, and thus we would end up with a blank
3622            // Keyguard.
3623            mNotificationPanel.setTouchDisabled(true);
3624        }
3625        instantExpandNotificationsPanel();
3626        mLeaveOpenOnKeyguardHide = false;
3627        if (mDraggedDownRow != null) {
3628            mDraggedDownRow.setUserLocked(false);
3629            mDraggedDownRow.notifyHeightChanged();
3630            mDraggedDownRow = null;
3631        }
3632    }
3633
3634    public boolean isCollapsing() {
3635        return mNotificationPanel.isCollapsing();
3636    }
3637
3638    public void addPostCollapseAction(Runnable r) {
3639        mPostCollapseRunnables.add(r);
3640    }
3641
3642    public boolean isInLaunchTransition() {
3643        return mNotificationPanel.isLaunchTransitionRunning()
3644                || mNotificationPanel.isLaunchTransitionFinished();
3645    }
3646
3647    /**
3648     * Fades the content of the keyguard away after the launch transition is done.
3649     *
3650     * @param beforeFading the runnable to be run when the circle is fully expanded and the fading
3651     *                     starts
3652     * @param endRunnable the runnable to be run when the transition is done
3653     */
3654    public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading,
3655            Runnable endRunnable) {
3656        mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
3657        mLaunchTransitionEndRunnable = endRunnable;
3658        Runnable hideRunnable = new Runnable() {
3659            @Override
3660            public void run() {
3661                mLaunchTransitionFadingAway = true;
3662                if (beforeFading != null) {
3663                    beforeFading.run();
3664                }
3665                mNotificationPanel.setAlpha(1);
3666                mNotificationPanel.animate()
3667                        .alpha(0)
3668                        .setStartDelay(FADE_KEYGUARD_START_DELAY)
3669                        .setDuration(FADE_KEYGUARD_DURATION)
3670                        .withLayer()
3671                        .withEndAction(new Runnable() {
3672                            @Override
3673                            public void run() {
3674                                mNotificationPanel.setAlpha(1);
3675                                runLaunchTransitionEndRunnable();
3676                                mLaunchTransitionFadingAway = false;
3677                            }
3678                        });
3679            }
3680        };
3681        if (mNotificationPanel.isLaunchTransitionRunning()) {
3682            mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable);
3683        } else {
3684            hideRunnable.run();
3685        }
3686    }
3687
3688    /**
3689     * Starts the timeout when we try to start the affordances on Keyguard. We usually rely that
3690     * Keyguard goes away via fadeKeyguardAfterLaunchTransition, however, that might not happen
3691     * because the launched app crashed or something else went wrong.
3692     */
3693    public void startLaunchTransitionTimeout() {
3694        mHandler.sendEmptyMessageDelayed(MSG_LAUNCH_TRANSITION_TIMEOUT,
3695                LAUNCH_TRANSITION_TIMEOUT_MS);
3696    }
3697
3698    private void onLaunchTransitionTimeout() {
3699        Log.w(TAG, "Launch transition: Timeout!");
3700        mNotificationPanel.resetViews();
3701    }
3702
3703    private void runLaunchTransitionEndRunnable() {
3704        if (mLaunchTransitionEndRunnable != null) {
3705            Runnable r = mLaunchTransitionEndRunnable;
3706
3707            // mLaunchTransitionEndRunnable might call showKeyguard, which would execute it again,
3708            // which would lead to infinite recursion. Protect against it.
3709            mLaunchTransitionEndRunnable = null;
3710            r.run();
3711        }
3712    }
3713
3714    /**
3715     * @return true if we would like to stay in the shade, false if it should go away entirely
3716     */
3717    public boolean hideKeyguard() {
3718        boolean staying = mLeaveOpenOnKeyguardHide;
3719        setBarState(StatusBarState.SHADE);
3720        if (mLeaveOpenOnKeyguardHide) {
3721            mLeaveOpenOnKeyguardHide = false;
3722            mNotificationPanel.animateToFullShade(calculateGoingToFullShadeDelay());
3723            if (mDraggedDownRow != null) {
3724                mDraggedDownRow.setUserLocked(false);
3725                mDraggedDownRow = null;
3726            }
3727        } else {
3728            instantCollapseNotificationPanel();
3729        }
3730        updateKeyguardState(staying, false /* fromShadeLocked */);
3731
3732        // Keyguard state has changed, but QS is not listening anymore. Make sure to update the tile
3733        // visibilities so next time we open the panel we know the correct height already.
3734        if (mQSPanel != null) {
3735            mQSPanel.refreshAllTiles();
3736        }
3737        mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
3738        return staying;
3739    }
3740
3741    public long calculateGoingToFullShadeDelay() {
3742        return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration;
3743    }
3744
3745    /**
3746     * Notifies the status bar the Keyguard is fading away with the specified timings.
3747     *
3748     * @param delay the animation delay in miliseconds
3749     * @param fadeoutDuration the duration of the exit animation, in milliseconds
3750     */
3751    public void setKeyguardFadingAway(long delay, long fadeoutDuration) {
3752        mKeyguardFadingAway = true;
3753        mKeyguardFadingAwayDelay = delay;
3754        mKeyguardFadingAwayDuration = fadeoutDuration;
3755        mWaitingForKeyguardExit = false;
3756        disable(mDisabledUnmodified, true /* animate */);
3757    }
3758
3759    public boolean isKeyguardFadingAway() {
3760        return mKeyguardFadingAway;
3761    }
3762
3763    /**
3764     * Notifies that the Keyguard fading away animation is done.
3765     */
3766    public void finishKeyguardFadingAway() {
3767        mKeyguardFadingAway = false;
3768    }
3769
3770    private void updatePublicMode() {
3771        setLockscreenPublicMode(mStatusBarKeyguardViewManager.isShowing()
3772                && mStatusBarKeyguardViewManager.isSecure(mCurrentUserId));
3773    }
3774
3775    private void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) {
3776        if (mState == StatusBarState.KEYGUARD) {
3777            mKeyguardIndicationController.setVisible(true);
3778            mNotificationPanel.resetViews();
3779            mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked);
3780        } else {
3781            mKeyguardIndicationController.setVisible(false);
3782            mKeyguardUserSwitcher.setKeyguard(false,
3783                    goingToFullShade || mState == StatusBarState.SHADE_LOCKED || fromShadeLocked);
3784        }
3785        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
3786            mScrimController.setKeyguardShowing(true);
3787        } else {
3788            mScrimController.setKeyguardShowing(false);
3789        }
3790        mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade);
3791        updateDozingState();
3792        updatePublicMode();
3793        updateStackScrollerState(goingToFullShade);
3794        updateNotifications();
3795        checkBarModes();
3796        updateCarrierLabelVisibility(false);
3797        updateMediaMetaData(false);
3798        mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
3799                mStatusBarKeyguardViewManager.isSecure());
3800    }
3801
3802    private void updateDozingState() {
3803        if (mState != StatusBarState.KEYGUARD && !mNotificationPanel.isDozing()) {
3804            return;
3805        }
3806        boolean animate = !mDozing && mDozeScrimController.isPulsing();
3807        mNotificationPanel.setDozing(mDozing, animate);
3808        mStackScroller.setDark(mDozing, animate, mScreenOnTouchLocation);
3809        mScrimController.setDozing(mDozing);
3810        mDozeScrimController.setDozing(mDozing, animate);
3811    }
3812
3813    public void updateStackScrollerState(boolean goingToFullShade) {
3814        if (mStackScroller == null) return;
3815        boolean onKeyguard = mState == StatusBarState.KEYGUARD;
3816        mStackScroller.setHideSensitive(isLockscreenPublicMode(), goingToFullShade);
3817        mStackScroller.setDimmed(onKeyguard, false /* animate */);
3818        mStackScroller.setExpandingEnabled(!onKeyguard);
3819        ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild();
3820        mStackScroller.setActivatedChild(null);
3821        if (activatedChild != null) {
3822            activatedChild.makeInactive(false /* animate */);
3823        }
3824    }
3825
3826    public void userActivity() {
3827        if (mState == StatusBarState.KEYGUARD) {
3828            mKeyguardViewMediatorCallback.userActivity();
3829        }
3830    }
3831
3832    public boolean interceptMediaKey(KeyEvent event) {
3833        return mState == StatusBarState.KEYGUARD
3834                && mStatusBarKeyguardViewManager.interceptMediaKey(event);
3835    }
3836
3837    public boolean onMenuPressed() {
3838        return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.onMenuPressed();
3839    }
3840
3841    public boolean onBackPressed() {
3842        if (mStatusBarKeyguardViewManager.onBackPressed()) {
3843            return true;
3844        }
3845        if (mNotificationPanel.isQsExpanded()) {
3846            if (mNotificationPanel.isQsDetailShowing()) {
3847                mNotificationPanel.closeQsDetail();
3848            } else {
3849                mNotificationPanel.animateCloseQs();
3850            }
3851            return true;
3852        }
3853        if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
3854            animateCollapsePanels();
3855            return true;
3856        }
3857        return false;
3858    }
3859
3860    public boolean onSpacePressed() {
3861        if (mScreenOn != null && mScreenOn
3862                && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
3863            animateCollapsePanels(
3864                    CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */);
3865            return true;
3866        }
3867        return false;
3868    }
3869
3870    private void showBouncer() {
3871        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
3872            mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing();
3873            mStatusBarKeyguardViewManager.dismiss();
3874        }
3875    }
3876
3877    private void instantExpandNotificationsPanel() {
3878
3879        // Make our window larger and the panel expanded.
3880        makeExpandedVisible(true);
3881        mNotificationPanel.instantExpand();
3882    }
3883
3884    private void instantCollapseNotificationPanel() {
3885        mNotificationPanel.instantCollapse();
3886    }
3887
3888    @Override
3889    public void onActivated(ActivatableNotificationView view) {
3890        EventLogTags.writeSysuiLockscreenGesture(
3891                EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_TAP_NOTIFICATION_ACTIVATE,
3892                0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
3893        mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again);
3894        ActivatableNotificationView previousView = mStackScroller.getActivatedChild();
3895        if (previousView != null) {
3896            previousView.makeInactive(true /* animate */);
3897        }
3898        mStackScroller.setActivatedChild(view);
3899    }
3900
3901    /**
3902     * @param state The {@link StatusBarState} to set.
3903     */
3904    public void setBarState(int state) {
3905        // If we're visible and switched to SHADE_LOCKED (the user dragged
3906        // down on the lockscreen), clear notification LED, vibration,
3907        // ringing.
3908        // Other transitions are covered in handleVisibleToUserChanged().
3909        if (state != mState && mVisible && state == StatusBarState.SHADE_LOCKED) {
3910            try {
3911                mBarService.clearNotificationEffects();
3912            } catch (RemoteException e) {
3913                // Ignore.
3914            }
3915        }
3916        mState = state;
3917        mStatusBarWindowManager.setStatusBarState(state);
3918    }
3919
3920    @Override
3921    public void onActivationReset(ActivatableNotificationView view) {
3922        if (view == mStackScroller.getActivatedChild()) {
3923            mKeyguardIndicationController.hideTransientIndication();
3924            mStackScroller.setActivatedChild(null);
3925        }
3926    }
3927
3928    public void onTrackingStarted() {
3929        runPostCollapseRunnables();
3930    }
3931
3932    public void onClosingFinished() {
3933        runPostCollapseRunnables();
3934    }
3935
3936    public void onUnlockHintStarted() {
3937        mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock);
3938    }
3939
3940    public void onHintFinished() {
3941        // Delay the reset a bit so the user can read the text.
3942        mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS);
3943    }
3944
3945    public void onCameraHintStarted() {
3946        mKeyguardIndicationController.showTransientIndication(R.string.camera_hint);
3947    }
3948
3949    public void onPhoneHintStarted() {
3950        mKeyguardIndicationController.showTransientIndication(R.string.phone_hint);
3951    }
3952
3953    public void onTrackingStopped(boolean expand) {
3954        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
3955            if (!expand && !mUnlockMethodCache.isCurrentlyInsecure()) {
3956                showBouncer();
3957            }
3958        }
3959    }
3960
3961    @Override
3962    protected int getMaxKeyguardNotifications() {
3963        return mKeyguardMaxNotificationCount;
3964    }
3965
3966    public NavigationBarView getNavigationBarView() {
3967        return mNavigationBarView;
3968    }
3969
3970    // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
3971
3972    @Override
3973    public boolean onDraggedDown(View startingChild, int dragLengthY) {
3974        if (hasActiveNotifications()) {
3975            EventLogTags.writeSysuiLockscreenGesture(
3976                    EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_DOWN_FULL_SHADE,
3977                    (int) (dragLengthY / mDisplayMetrics.density),
3978                    0 /* velocityDp - N/A */);
3979
3980            // We have notifications, go to locked shade.
3981            goToLockedShade(startingChild);
3982            return true;
3983        } else {
3984
3985            // No notifications - abort gesture.
3986            return false;
3987        }
3988    }
3989
3990    @Override
3991    public void onDragDownReset() {
3992        mStackScroller.setDimmed(true /* dimmed */, true /* animated */);
3993    }
3994
3995    @Override
3996    public void onThresholdReached() {
3997        mStackScroller.setDimmed(false /* dimmed */, true /* animate */);
3998    }
3999
4000    @Override
4001    public void onTouchSlopExceeded() {
4002        mStackScroller.removeLongPressCallback();
4003    }
4004
4005    @Override
4006    public void setEmptyDragAmount(float amount) {
4007        mNotificationPanel.setEmptyDragAmount(amount);
4008    }
4009
4010    /**
4011     * If secure with redaction: Show bouncer, go to unlocked shade.
4012     *
4013     * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p>
4014     *
4015     * @param expandView The view to expand after going to the shade.
4016     */
4017    public void goToLockedShade(View expandView) {
4018        ExpandableNotificationRow row = null;
4019        if (expandView instanceof ExpandableNotificationRow) {
4020            row = (ExpandableNotificationRow) expandView;
4021            row.setUserExpanded(true);
4022        }
4023        boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId)
4024                || !mShowLockscreenNotifications;
4025        if (isLockscreenPublicMode() && fullShadeNeedsBouncer) {
4026            mLeaveOpenOnKeyguardHide = true;
4027            showBouncer();
4028            mDraggedDownRow = row;
4029        } else {
4030            mNotificationPanel.animateToFullShade(0 /* delay */);
4031            setBarState(StatusBarState.SHADE_LOCKED);
4032            updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
4033            if (row != null) {
4034                row.setUserLocked(false);
4035            }
4036        }
4037    }
4038
4039    /**
4040     * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}.
4041     */
4042    public void goToKeyguard() {
4043        if (mState == StatusBarState.SHADE_LOCKED) {
4044            mStackScroller.onGoToKeyguard();
4045            setBarState(StatusBarState.KEYGUARD);
4046            updateKeyguardState(false /* goingToFullShade */, true /* fromShadeLocked*/);
4047        }
4048    }
4049
4050    /**
4051     * @return a ViewGroup that spans the entire panel which contains the quick settings
4052     */
4053    public ViewGroup getQuickSettingsOverlayParent() {
4054        return mNotificationPanel;
4055    }
4056
4057    public long getKeyguardFadingAwayDelay() {
4058        return mKeyguardFadingAwayDelay;
4059    }
4060
4061    public long getKeyguardFadingAwayDuration() {
4062        return mKeyguardFadingAwayDuration;
4063    }
4064
4065    public LinearLayout getSystemIcons() {
4066        return mSystemIcons;
4067    }
4068
4069    public LinearLayout getSystemIconArea() {
4070        return mSystemIconArea;
4071    }
4072
4073    @Override
4074    public void setBouncerShowing(boolean bouncerShowing) {
4075        super.setBouncerShowing(bouncerShowing);
4076        disable(mDisabledUnmodified, true /* animate */);
4077    }
4078
4079    public void onScreenTurnedOff() {
4080        mScreenOnFromKeyguard = false;
4081        mScreenOnComingFromTouch = false;
4082        mScreenOnTouchLocation = null;
4083        mStackScroller.setAnimationsEnabled(false);
4084        updateVisibleToUser();
4085    }
4086
4087    public void onScreenTurnedOn() {
4088        mScreenOnFromKeyguard = true;
4089        mStackScroller.setAnimationsEnabled(true);
4090        mNotificationPanel.onScreenTurnedOn();
4091        mNotificationPanel.setTouchDisabled(false);
4092        updateVisibleToUser();
4093    }
4094
4095    /**
4096     * This handles long-press of both back and recents.  They are
4097     * handled together to capture them both being long-pressed
4098     * at the same time to exit screen pinning (lock task).
4099     *
4100     * When accessibility mode is on, only a long-press from recents
4101     * is required to exit.
4102     *
4103     * In all other circumstances we try to pass through long-press events
4104     * for Back, so that apps can still use it.  Which can be from two things.
4105     * 1) Not currently in screen pinning (lock task).
4106     * 2) Back is long-pressed without recents.
4107     */
4108    private void handleLongPressBackRecents(View v) {
4109        try {
4110            boolean sendBackLongPress = false;
4111            IActivityManager activityManager = ActivityManagerNative.getDefault();
4112            boolean isAccessiblityEnabled = mAccessibilityManager.isEnabled();
4113            if (activityManager.isInLockTaskMode() && !isAccessiblityEnabled) {
4114                long time = System.currentTimeMillis();
4115                // If we recently long-pressed the other button then they were
4116                // long-pressed 'together'
4117                if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
4118                    activityManager.stopLockTaskModeOnCurrent();
4119                    // When exiting refresh disabled flags.
4120                    mNavigationBarView.setDisabledFlags(mDisabled, true);
4121                } else if ((v.getId() == R.id.back)
4122                        && !mNavigationBarView.getRecentsButton().isPressed()) {
4123                    // If we aren't pressing recents right now then they presses
4124                    // won't be together, so send the standard long-press action.
4125                    sendBackLongPress = true;
4126                }
4127                mLastLockToAppLongPress = time;
4128            } else {
4129                // If this is back still need to handle sending the long-press event.
4130                if (v.getId() == R.id.back) {
4131                    sendBackLongPress = true;
4132                } else if (isAccessiblityEnabled && activityManager.isInLockTaskMode()) {
4133                    // When in accessibility mode a long press that is recents (not back)
4134                    // should stop lock task.
4135                    activityManager.stopLockTaskModeOnCurrent();
4136                    // When exiting refresh disabled flags.
4137                    mNavigationBarView.setDisabledFlags(mDisabled, true);
4138                }
4139            }
4140            if (sendBackLongPress) {
4141                KeyButtonView keyButtonView = (KeyButtonView) v;
4142                keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
4143                keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
4144            }
4145        } catch (RemoteException e) {
4146            Log.d(TAG, "Unable to reach activity manager", e);
4147        }
4148    }
4149
4150    // Recents
4151
4152    @Override
4153    protected void showRecents(boolean triggeredFromAltTab) {
4154        // Set the recents visibility flag
4155        mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
4156        notifyUiVisibilityChanged(mSystemUiVisibility);
4157        super.showRecents(triggeredFromAltTab);
4158    }
4159
4160    @Override
4161    protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
4162        // Unset the recents visibility flag
4163        mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
4164        notifyUiVisibilityChanged(mSystemUiVisibility);
4165        super.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
4166    }
4167
4168    @Override
4169    protected void toggleRecents() {
4170        // Toggle the recents visibility flag
4171        mSystemUiVisibility ^= View.RECENT_APPS_VISIBLE;
4172        notifyUiVisibilityChanged(mSystemUiVisibility);
4173        super.toggleRecents();
4174    }
4175
4176    @Override
4177    public void onVisibilityChanged(boolean visible) {
4178        // Update the recents visibility flag
4179        if (visible) {
4180            mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
4181        } else {
4182            mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
4183        }
4184        notifyUiVisibilityChanged(mSystemUiVisibility);
4185    }
4186
4187    @Override
4188    public void showScreenPinningRequest() {
4189        if (mKeyguardMonitor.isShowing()) {
4190            // Don't allow apps to trigger this from keyguard.
4191            return;
4192        }
4193        // Show screen pinning request, since this comes from an app, show 'no thanks', button.
4194        showScreenPinningRequest(true);
4195    }
4196
4197    public void showScreenPinningRequest(boolean allowCancel) {
4198        mScreenPinningRequest.showPrompt(allowCancel);
4199    }
4200
4201    public boolean hasActiveNotifications() {
4202        return !mNotificationData.getActiveNotifications().isEmpty();
4203    }
4204
4205    public void wakeUpIfDozing(long time, MotionEvent event) {
4206        if (mDozing && mDozeScrimController.isPulsing()) {
4207            PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
4208            pm.wakeUp(time);
4209            mScreenOnComingFromTouch = true;
4210            mScreenOnTouchLocation = new PointF(event.getX(), event.getY());
4211            mNotificationPanel.setTouchDisabled(false);
4212        }
4213    }
4214
4215    private final class ShadeUpdates {
4216        private final ArraySet<String> mVisibleNotifications = new ArraySet<String>();
4217        private final ArraySet<String> mNewVisibleNotifications = new ArraySet<String>();
4218
4219        public void check() {
4220            mNewVisibleNotifications.clear();
4221            ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
4222            for (int i = 0; i < activeNotifications.size(); i++) {
4223                final Entry entry = activeNotifications.get(i);
4224                final boolean visible = entry.row != null
4225                        && entry.row.getVisibility() == View.VISIBLE;
4226                if (visible) {
4227                    mNewVisibleNotifications.add(entry.key + entry.notification.getPostTime());
4228                }
4229            }
4230            final boolean updates = !mVisibleNotifications.containsAll(mNewVisibleNotifications);
4231            mVisibleNotifications.clear();
4232            mVisibleNotifications.addAll(mNewVisibleNotifications);
4233
4234            // We have new notifications
4235            if (updates && mDozeServiceHost != null) {
4236                mDozeServiceHost.fireNewNotifications();
4237            }
4238        }
4239    }
4240
4241    private final class DozeServiceHost implements DozeHost {
4242        // Amount of time to allow to update the time shown on the screen before releasing
4243        // the wakelock.  This timeout is design to compensate for the fact that we don't
4244        // currently have a way to know when time display contents have actually been
4245        // refreshed once we've finished rendering a new frame.
4246        private static final long PROCESSING_TIME = 500;
4247
4248        private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
4249        private final H mHandler = new H();
4250
4251        // Keeps the last reported state by fireNotificationLight.
4252        private boolean mNotificationLightOn;
4253
4254        @Override
4255        public String toString() {
4256            return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]";
4257        }
4258
4259        public void firePowerSaveChanged(boolean active) {
4260            for (Callback callback : mCallbacks) {
4261                callback.onPowerSaveChanged(active);
4262            }
4263        }
4264
4265        public void fireBuzzBeepBlinked() {
4266            for (Callback callback : mCallbacks) {
4267                callback.onBuzzBeepBlinked();
4268            }
4269        }
4270
4271        public void fireNotificationLight(boolean on) {
4272            mNotificationLightOn = on;
4273            for (Callback callback : mCallbacks) {
4274                callback.onNotificationLight(on);
4275            }
4276        }
4277
4278        public void fireNewNotifications() {
4279            for (Callback callback : mCallbacks) {
4280                callback.onNewNotifications();
4281            }
4282        }
4283
4284        @Override
4285        public void addCallback(@NonNull Callback callback) {
4286            mCallbacks.add(callback);
4287        }
4288
4289        @Override
4290        public void removeCallback(@NonNull Callback callback) {
4291            mCallbacks.remove(callback);
4292        }
4293
4294        @Override
4295        public void startDozing(@NonNull Runnable ready) {
4296            mHandler.obtainMessage(H.MSG_START_DOZING, ready).sendToTarget();
4297        }
4298
4299        @Override
4300        public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
4301            mHandler.obtainMessage(H.MSG_PULSE_WHILE_DOZING, reason, 0, callback).sendToTarget();
4302        }
4303
4304        @Override
4305        public void stopDozing() {
4306            mHandler.obtainMessage(H.MSG_STOP_DOZING).sendToTarget();
4307        }
4308
4309        @Override
4310        public boolean isPowerSaveActive() {
4311            return mBatteryController != null && mBatteryController.isPowerSave();
4312        }
4313
4314        @Override
4315        public boolean isNotificationLightOn() {
4316            return mNotificationLightOn;
4317        }
4318
4319        private void handleStartDozing(@NonNull Runnable ready) {
4320            if (!mDozing) {
4321                mDozing = true;
4322                DozeLog.traceDozing(mContext, mDozing);
4323                updateDozingState();
4324            }
4325            ready.run();
4326        }
4327
4328        private void handlePulseWhileDozing(@NonNull PulseCallback callback, int reason) {
4329            mDozeScrimController.pulse(callback, reason);
4330        }
4331
4332        private void handleStopDozing() {
4333            if (mDozing) {
4334                mDozing = false;
4335                DozeLog.traceDozing(mContext, mDozing);
4336                updateDozingState();
4337            }
4338        }
4339
4340        private final class H extends Handler {
4341            private static final int MSG_START_DOZING = 1;
4342            private static final int MSG_PULSE_WHILE_DOZING = 2;
4343            private static final int MSG_STOP_DOZING = 3;
4344
4345            @Override
4346            public void handleMessage(Message msg) {
4347                switch (msg.what) {
4348                    case MSG_START_DOZING:
4349                        handleStartDozing((Runnable) msg.obj);
4350                        break;
4351                    case MSG_PULSE_WHILE_DOZING:
4352                        handlePulseWhileDozing((PulseCallback) msg.obj, msg.arg1);
4353                        break;
4354                    case MSG_STOP_DOZING:
4355                        handleStopDozing();
4356                        break;
4357                }
4358            }
4359        }
4360    }
4361}
4362