/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar.phone; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.Notification; import android.app.PendingIntent; import android.app.StatusBarManager; import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.IPackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PointF; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.inputmethodservice.InputMethodService; import android.media.AudioAttributes; import android.media.MediaMetadata; import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Message; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.view.Display; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.ThreadedRenderer; import android.view.VelocityTracker; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.ViewStub; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; import android.view.animation.PathInterpolator; import android.widget.ImageView; import android.widget.TextView; import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.statusbar.StatusBarIcon; import com.android.keyguard.KeyguardHostView.OnDismissAction; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.BatteryMeterView; import com.android.systemui.DemoMode; import com.android.systemui.EventLogConstants; import com.android.systemui.EventLogTags; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.assist.AssistManager; import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.qs.QSPanel; import com.android.systemui.recents.ScreenPinningRequest; import com.android.systemui.statusbar.ActivatableNotificationView; import com.android.systemui.statusbar.BackDropView; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DismissView; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.NotificationData.Entry; import com.android.systemui.statusbar.NotificationOverflowContainer; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.SignalClusterView; import com.android.systemui.statusbar.SpeedBumpView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener; import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; import com.android.systemui.statusbar.policy.BluetoothControllerImpl; import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.statusbar.policy.CastControllerImpl; import com.android.systemui.statusbar.policy.FlashlightController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.HotspotControllerImpl; import com.android.systemui.statusbar.policy.KeyButtonView; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; import com.android.systemui.statusbar.policy.LocationControllerImpl; import com.android.systemui.statusbar.policy.NetworkControllerImpl; import com.android.systemui.statusbar.policy.NextAlarmController; import com.android.systemui.statusbar.policy.PreviewInflater; import com.android.systemui.statusbar.policy.RotationLockControllerImpl; import com.android.systemui.statusbar.policy.SecurityControllerImpl; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener; import com.android.systemui.statusbar.stack.StackViewState; import com.android.systemui.volume.VolumeComponent; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.TreeSet; import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.StatusBarManager.windowStateToString; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING; public class PhoneStatusBar extends BaseStatusBar implements DemoMode, DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener, HeadsUpManager.OnHeadsUpChangedListener { static final String TAG = "PhoneStatusBar"; public static final boolean DEBUG = BaseStatusBar.DEBUG; public static final boolean SPEW = false; public static final boolean DUMPTRUCK = true; // extra dumpsys info public static final boolean DEBUG_GESTURES = false; public static final boolean DEBUG_MEDIA = false; public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false; public static final boolean DEBUG_WINDOW_STATE = false; // additional instrumentation for testing purposes; intended to be left on during development public static final boolean CHATTY = DEBUG; public static final boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true; public static final String ACTION_FAKE_ARTWORK = "fake_artwork"; private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000; private static final int MSG_CLOSE_PANELS = 1001; private static final int MSG_OPEN_SETTINGS_PANEL = 1002; private static final int MSG_LAUNCH_TRANSITION_TIMEOUT = 1003; // 1020-1040 reserved for BaseStatusBar // Time after we abort the launch transition. private static final long LAUNCH_TRANSITION_TIMEOUT_MS = 5000; private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true; private static final int STATUS_OR_NAV_TRANSIENT = View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT; private static final long AUTOHIDE_TIMEOUT_MS = 3000; /** The minimum delay in ms between reports of notification visibility. */ private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500; /** * The delay to reset the hint text when the hint animation is finished running. */ private static final int HINT_RESET_DELAY_MS = 1200; private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) .build(); public static final int FADE_KEYGUARD_START_DELAY = 100; public static final int FADE_KEYGUARD_DURATION = 300; /** Allow some time inbetween the long press for back and recents. */ private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200; /** If true, the system is in the half-boot-to-decryption-screen state. * Prudently disable QS and notifications. */ private static final boolean ONLY_CORE_APPS; static { boolean onlyCoreApps; try { onlyCoreApps = IPackageManager.Stub.asInterface(ServiceManager.getService("package")) .isOnlyCoreApps(); } catch (RemoteException e) { onlyCoreApps = false; } ONLY_CORE_APPS = onlyCoreApps; } PhoneStatusBarPolicy mIconPolicy; // These are no longer handled by the policy, because we need custom strategies for them BluetoothControllerImpl mBluetoothController; SecurityControllerImpl mSecurityController; BatteryController mBatteryController; LocationControllerImpl mLocationController; NetworkControllerImpl mNetworkController; HotspotControllerImpl mHotspotController; RotationLockControllerImpl mRotationLockController; UserInfoController mUserInfoController; ZenModeController mZenModeController; CastControllerImpl mCastController; VolumeComponent mVolumeComponent; KeyguardUserSwitcher mKeyguardUserSwitcher; FlashlightController mFlashlightController; UserSwitcherController mUserSwitcherController; NextAlarmController mNextAlarmController; KeyguardMonitor mKeyguardMonitor; BrightnessMirrorController mBrightnessMirrorController; AccessibilityController mAccessibilityController; int mNaturalBarHeight = -1; Display mDisplay; Point mCurrentDisplaySize = new Point(); StatusBarWindowView mStatusBarWindow; PhoneStatusBarView mStatusBarView; private int mStatusBarWindowState = WINDOW_STATE_SHOWING; private StatusBarWindowManager mStatusBarWindowManager; private UnlockMethodCache mUnlockMethodCache; private DozeServiceHost mDozeServiceHost; private boolean mScreenOnComingFromTouch; private PointF mScreenOnTouchLocation; int mPixelFormat; Object mQueueLock = new Object(); StatusBarIconController mIconController; // expanded notifications NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window View mExpandedContents; TextView mNotificationPanelDebugText; // settings private QSPanel mQSPanel; // top bar StatusBarHeaderView mHeader; KeyguardStatusBarView mKeyguardStatusBar; View mKeyguardStatusView; KeyguardBottomAreaView mKeyguardBottomArea; boolean mLeaveOpenOnKeyguardHide; KeyguardIndicationController mKeyguardIndicationController; private boolean mKeyguardFadingAway; private long mKeyguardFadingAwayDelay; private long mKeyguardFadingAwayDuration; int mKeyguardMaxNotificationCount; boolean mExpandedVisible; private int mNavigationBarWindowState = WINDOW_STATE_SHOWING; // the tracker view int mTrackingPosition; // the position of the top of the tracking view. // Tracking finger for opening/closing. boolean mTracking; VelocityTracker mVelocityTracker; int[] mAbsPos = new int[2]; ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>(); // for disabling the status bar int mDisabled1 = 0; int mDisabled2 = 0; // tracking calls to View.setSystemUiVisibility() int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; // last value sent to window manager private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE; DisplayMetrics mDisplayMetrics = new DisplayMetrics(); // XXX: gesture research private final GestureRecorder mGestureRec = DEBUG_GESTURES ? new GestureRecorder("/sdcard/statusbar_gestures.dat") : null; private ScreenPinningRequest mScreenPinningRequest; private int mNavigationIconHints = 0; private HandlerThread mHandlerThread; // ensure quick settings is disabled until the current user makes it through the setup wizard private boolean mUserSetup = false; private ContentObserver mUserSetupObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { final boolean userSetup = 0 != Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 0 /*default */, mCurrentUserId); if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " + "selfChange=%s userSetup=%s mUserSetup=%s", selfChange, userSetup, mUserSetup)); if (userSetup != mUserSetup) { mUserSetup = userSetup; if (!mUserSetup && mStatusBarView != null) animateCollapseQuickSettings(); if (mKeyguardBottomArea != null) { mKeyguardBottomArea.setUserSetupComplete(mUserSetup); } } if (mIconPolicy != null) { mIconPolicy.setCurrentUserSetup(mUserSetup); } } }; final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { boolean wasUsing = mUseHeadsUp; mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt( mContext.getContentResolver(), Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, Settings.Global.HEADS_UP_OFF); mHeadsUpTicker = mUseHeadsUp && 0 != Settings.Global.getInt( mContext.getContentResolver(), SETTING_HEADS_UP_TICKER, 0); Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled")); if (wasUsing != mUseHeadsUp) { if (!mUseHeadsUp) { Log.d(TAG, "dismissing any existing heads up notification on disable event"); mHeadsUpManager.releaseAllImmediately(); } } } }; private int mInteractingWindows; private boolean mAutohideSuspended; private int mStatusBarMode; private int mNavigationBarMode; private ViewMediatorCallback mKeyguardViewMediatorCallback; private ScrimController mScrimController; private DozeScrimController mDozeScrimController; private final Runnable mAutohide = new Runnable() { @Override public void run() { int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT; if (mSystemUiVisibility != requested) { notifyUiVisibilityChanged(requested); } }}; private boolean mWaitingForKeyguardExit; private boolean mDozing; private boolean mDozingRequested; private boolean mScrimSrcModeEnabled; private Interpolator mLinearInterpolator = new LinearInterpolator(); private Interpolator mBackdropInterpolator = new AccelerateDecelerateInterpolator(); public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f); public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f); private BackDropView mBackdrop; private ImageView mBackdropFront, mBackdropBack; private PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC); private PorterDuffXfermode mSrcOverXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER); private MediaSessionManager mMediaSessionManager; private MediaController mMediaController; private String mMediaNotificationKey; private MediaMetadata mMediaMetadata; private MediaController.Callback mMediaListener = new MediaController.Callback() { @Override public void onPlaybackStateChanged(PlaybackState state) { super.onPlaybackStateChanged(state); if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state); if (state != null) { if (!isPlaybackActive(state.getState())) { clearCurrentMediaNotification(); updateMediaMetaData(true); } } } @Override public void onMetadataChanged(MediaMetadata metadata) { super.onMetadataChanged(metadata); if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata); mMediaMetadata = metadata; updateMediaMetaData(true); } }; private final OnChildLocationsChangedListener mOnChildLocationsChangedListener = new OnChildLocationsChangedListener() { @Override public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) { userActivity(); } }; private int mDisabledUnmodified1; private int mDisabledUnmodified2; /** Keys of notifications currently visible to the user. */ private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications = new ArraySet<>(); private long mLastVisibilityReportUptimeMs; private final ShadeUpdates mShadeUpdates = new ShadeUpdates(); private int mDrawCount; private Runnable mLaunchTransitionEndRunnable; private boolean mLaunchTransitionFadingAway; private ExpandableNotificationRow mDraggedDownRow; // Fingerprint (as computed by getLoggingFingerprint() of the last logged state. private int mLastLoggedStateFingerprint; private static final int VISIBLE_LOCATIONS = StackViewState.LOCATION_FIRST_CARD | StackViewState.LOCATION_MAIN_AREA; private final OnChildLocationsChangedListener mNotificationLocationsChangedListener = new OnChildLocationsChangedListener() { @Override public void onChildLocationsChanged( NotificationStackScrollLayout stackScrollLayout) { if (mHandler.hasCallbacks(mVisibilityReporter)) { // Visibilities will be reported when the existing // callback is executed. return; } // Calculate when we're allowed to run the visibility // reporter. Note that this timestamp might already have // passed. That's OK, the callback will just be executed // ASAP. long nextReportUptimeMs = mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS; mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs); } }; // Tracks notifications currently visible in mNotificationStackScroller and // emits visibility events via NoMan on changes. private final Runnable mVisibilityReporter = new Runnable() { private final ArraySet<NotificationVisibility> mTmpNewlyVisibleNotifications = new ArraySet<>(); private final ArraySet<NotificationVisibility> mTmpCurrentlyVisibleNotifications = new ArraySet<>(); private final ArraySet<NotificationVisibility> mTmpNoLongerVisibleNotifications = new ArraySet<>(); @Override public void run() { mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis(); final String mediaKey = getCurrentMediaNotificationKey(); // 1. Loop over mNotificationData entries: // A. Keep list of visible notifications. // B. Keep list of previously hidden, now visible notifications. // 2. Compute no-longer visible notifications by removing currently // visible notifications from the set of previously visible // notifications. // 3. Report newly visible and no-longer visible notifications. // 4. Keep currently visible notifications for next report. ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); int N = activeNotifications.size(); for (int i = 0; i < N; i++) { Entry entry = activeNotifications.get(i); String key = entry.notification.getKey(); boolean isVisible = (mStackScroller.getChildLocation(entry.row) & VISIBLE_LOCATIONS) != 0; NotificationVisibility visObj = NotificationVisibility.obtain(key, i, isVisible); boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj); if (isVisible) { // Build new set of visible notifications. mTmpCurrentlyVisibleNotifications.add(visObj); if (!previouslyVisible) { mTmpNewlyVisibleNotifications.add(visObj); } } else { // release object visObj.recycle(); } } mTmpNoLongerVisibleNotifications.addAll(mCurrentlyVisibleNotifications); mTmpNoLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications); logNotificationVisibilityChanges( mTmpNewlyVisibleNotifications, mTmpNoLongerVisibleNotifications); recycleAllVisibilityObjects(mCurrentlyVisibleNotifications); mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications); recycleAllVisibilityObjects(mTmpNoLongerVisibleNotifications); mTmpCurrentlyVisibleNotifications.clear(); mTmpNewlyVisibleNotifications.clear(); mTmpNoLongerVisibleNotifications.clear(); } }; private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) { final int N = array.size(); for (int i = 0 ; i < N; i++) { array.valueAt(i).recycle(); } array.clear(); } private final View.OnClickListener mOverflowClickListener = new View.OnClickListener() { @Override public void onClick(View v) { goToLockedShade(null); } }; private HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>> mTmpChildOrderMap = new HashMap<>(); private HashSet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new HashSet<>(); private RankingMap mLatestRankingMap; private boolean mNoAnimationOnNextBarModeChange; @Override public void start() { mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay(); updateDisplaySize(); mScrimSrcModeEnabled = mContext.getResources().getBoolean( R.bool.config_status_bar_scrim_behind_use_src); super.start(); // calls createAndAddWindows() mMediaSessionManager = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); // TODO: use MediaSessionManager.SessionListener to hook us up to future updates // in session state addNavigationBar(); // Lastly, call to the icon policy to install/update all the icons. mIconPolicy = new PhoneStatusBarPolicy(mContext, mCastController, mHotspotController, mUserInfoController, mBluetoothController); mIconPolicy.setCurrentUserSetup(mUserSetup); mSettingsObserver.onChange(false); // set up mHeadsUpObserver.onChange(true); // set up if (ENABLE_HEADS_UP) { mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true, mHeadsUpObserver); mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true, mHeadsUpObserver); } mUnlockMethodCache = UnlockMethodCache.getInstance(mContext); mUnlockMethodCache.addListener(this); startKeyguard(); mDozeServiceHost = new DozeServiceHost(); putComponent(DozeHost.class, mDozeServiceHost); putComponent(PhoneStatusBar.class, this); setControllerUsers(); notifyUserAboutHiddenNotifications(); mScreenPinningRequest = new ScreenPinningRequest(mContext); } // ================================================================================ // Constructing the view // ================================================================================ protected PhoneStatusBarView makeStatusBarView() { final Context context = mContext; Resources res = context.getResources(); updateDisplaySize(); // populates mDisplayMetrics updateResources(); mStatusBarWindow = (StatusBarWindowView) View.inflate(context, R.layout.super_status_bar, null); mStatusBarWindow.setService(this); mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { checkUserAutohide(v, event); if (event.getAction() == MotionEvent.ACTION_DOWN) { if (mExpandedVisible) { animateCollapsePanels(); } } return mStatusBarWindow.onTouchEvent(event); } }); mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar); mStatusBarView.setBar(this); PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder); mStatusBarView.setPanelHolder(holder); mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById( R.id.notification_panel); mNotificationPanel.setStatusBar(this); if (!ActivityManager.isHighEndGfx()) { mStatusBarWindow.setBackground(null); mNotificationPanel.setBackground(new FastColorDrawable(context.getColor( R.color.notification_panel_solid_background))); } mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow); mHeadsUpManager.setBar(this); mHeadsUpManager.addListener(this); mHeadsUpManager.addListener(mNotificationPanel); mNotificationPanel.setHeadsUpManager(mHeadsUpManager); mNotificationData.setHeadsUpManager(mHeadsUpManager); if (MULTIUSER_DEBUG) { mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById( R.id.header_debug_info); mNotificationPanelDebugText.setVisibility(View.VISIBLE); } try { boolean showNav = mWindowManagerService.hasNavigationBar(); if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav); if (showNav) { mNavigationBarView = (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null); mNavigationBarView.setDisabledFlags(mDisabled1); mNavigationBarView.setBar(this); mNavigationBarView.setOnVerticalChangedListener( new NavigationBarView.OnVerticalChangedListener() { @Override public void onVerticalChanged(boolean isVertical) { if (mAssistManager != null) { mAssistManager.onConfigurationChanged(); } mNotificationPanel.setQsScrimEnabled(!isVertical); } }); mNavigationBarView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { checkUserAutohide(v, event); return false; }}); } } catch (RemoteException ex) { // no window manager? good luck with that } mAssistManager = new AssistManager(this, context); // figure out which pixel-format to use for the status bar. mPixelFormat = PixelFormat.OPAQUE; mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById( R.id.notification_stack_scroller); mStackScroller.setLongPressListener(getNotificationLongClicker()); mStackScroller.setPhoneStatusBar(this); mStackScroller.setGroupManager(mGroupManager); mStackScroller.setHeadsUpManager(mHeadsUpManager); mGroupManager.setOnGroupChangeListener(mStackScroller); mKeyguardIconOverflowContainer = (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate( R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false); mKeyguardIconOverflowContainer.setOnActivatedListener(this); mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener); mStackScroller.setOverflowContainer(mKeyguardIconOverflowContainer); SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate( R.layout.status_bar_notification_speed_bump, mStackScroller, false); mStackScroller.setSpeedBumpView(speedBump); mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate( R.layout.status_bar_no_notifications, mStackScroller, false); mStackScroller.setEmptyShadeView(mEmptyShadeView); mDismissView = (DismissView) LayoutInflater.from(mContext).inflate( R.layout.status_bar_notification_dismiss_all, mStackScroller, false); mDismissView.setOnButtonClickListener(new View.OnClickListener() { @Override public void onClick(View v) { MetricsLogger.action(mContext, MetricsLogger.ACTION_DISMISS_ALL_NOTES); clearAllNotifications(); } }); mStackScroller.setDismissView(mDismissView); mExpandedContents = mStackScroller; mBackdrop = (BackDropView) mStatusBarWindow.findViewById(R.id.backdrop); mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front); mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back); ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind); ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front); View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim); mScrimController = new ScrimController(scrimBehind, scrimInFront, headsUpScrim, mScrimSrcModeEnabled); mHeadsUpManager.addListener(mScrimController); mStackScroller.setScrimController(mScrimController); mScrimController.setBackDropView(mBackdrop); mStatusBarView.setScrimController(mScrimController); mDozeScrimController = new DozeScrimController(mScrimController, context); mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header); mHeader.setActivityStarter(this); mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header); mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view); mKeyguardBottomArea = (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area); mKeyguardBottomArea.setActivityStarter(this); mKeyguardBottomArea.setAssistManager(mAssistManager); mKeyguardIndicationController = new KeyguardIndicationController(mContext, (KeyguardIndicationTextView) mStatusBarWindow.findViewById( R.id.keyguard_indication_text)); mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController); // set the inital view visibility setAreThereNotifications(); mIconController = new StatusBarIconController( mContext, mStatusBarView, mKeyguardStatusBar, this); // Background thread for any controllers that need it. mHandlerThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND); mHandlerThread.start(); // Other icons mLocationController = new LocationControllerImpl(mContext, mHandlerThread.getLooper()); // will post a notification mBatteryController = new BatteryController(mContext); mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() { @Override public void onPowerSaveChanged() { mHandler.post(mCheckBarModes); if (mDozeServiceHost != null) { mDozeServiceHost.firePowerSaveChanged(mBatteryController.isPowerSave()); } } @Override public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { // noop } }); mNetworkController = new NetworkControllerImpl(mContext, mHandlerThread.getLooper()); mHotspotController = new HotspotControllerImpl(mContext); mBluetoothController = new BluetoothControllerImpl(mContext, mHandlerThread.getLooper()); mSecurityController = new SecurityControllerImpl(mContext); if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) { mRotationLockController = new RotationLockControllerImpl(mContext); } mUserInfoController = new UserInfoController(mContext); mVolumeComponent = getComponent(VolumeComponent.class); if (mVolumeComponent != null) { mZenModeController = mVolumeComponent.getZenController(); } mCastController = new CastControllerImpl(mContext); final SignalClusterView signalCluster = (SignalClusterView) mStatusBarView.findViewById(R.id.signal_cluster); final SignalClusterView signalClusterKeyguard = (SignalClusterView) mKeyguardStatusBar.findViewById(R.id.signal_cluster); final SignalClusterView signalClusterQs = (SignalClusterView) mHeader.findViewById(R.id.signal_cluster); mNetworkController.addSignalCallback(signalCluster); mNetworkController.addSignalCallback(signalClusterKeyguard); mNetworkController.addSignalCallback(signalClusterQs); signalCluster.setSecurityController(mSecurityController); signalCluster.setNetworkController(mNetworkController); signalClusterKeyguard.setSecurityController(mSecurityController); signalClusterKeyguard.setNetworkController(mNetworkController); signalClusterQs.setSecurityController(mSecurityController); signalClusterQs.setNetworkController(mNetworkController); final boolean isAPhone = mNetworkController.hasVoiceCallingFeature(); if (isAPhone) { mNetworkController.addEmergencyListener(mHeader); } mFlashlightController = new FlashlightController(mContext); mKeyguardBottomArea.setFlashlightController(mFlashlightController); mKeyguardBottomArea.setPhoneStatusBar(this); mKeyguardBottomArea.setUserSetupComplete(mUserSetup); mAccessibilityController = new AccessibilityController(mContext); mKeyguardBottomArea.setAccessibilityController(mAccessibilityController); mNextAlarmController = new NextAlarmController(mContext); mKeyguardMonitor = new KeyguardMonitor(mContext); if (UserSwitcherController.isUserSwitcherAvailable(UserManager.get(mContext))) { mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor, mHandler); } mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext, (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), mKeyguardStatusBar, mNotificationPanel, mUserSwitcherController); // Set up the quick settings tile panel mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel); if (mQSPanel != null) { final QSTileHost qsh = new QSTileHost(mContext, this, mBluetoothController, mLocationController, mRotationLockController, mNetworkController, mZenModeController, mHotspotController, mCastController, mFlashlightController, mUserSwitcherController, mKeyguardMonitor, mSecurityController); mQSPanel.setHost(qsh); mQSPanel.setTiles(qsh.getTiles()); mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow); mQSPanel.setBrightnessMirror(mBrightnessMirrorController); mHeader.setQSPanel(mQSPanel); qsh.setCallback(new QSTileHost.Callback() { @Override public void onTilesChanged() { mQSPanel.setTiles(qsh.getTiles()); } }); } // User info. Trigger first load. mHeader.setUserInfoController(mUserInfoController); mKeyguardStatusBar.setUserInfoController(mUserInfoController); mKeyguardStatusBar.setUserSwitcherController(mUserSwitcherController); mUserInfoController.reloadUserInfo(); mHeader.setBatteryController(mBatteryController); ((BatteryMeterView) mStatusBarView.findViewById(R.id.battery)).setBatteryController( mBatteryController); mKeyguardStatusBar.setBatteryController(mBatteryController); mHeader.setNextAlarmController(mNextAlarmController); PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mBroadcastReceiver.onReceive(mContext, new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF)); // receive broadcasts IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); IntentFilter demoFilter = new IntentFilter(); if (DEBUG_MEDIA_FAKE_ARTWORK) { demoFilter.addAction(ACTION_FAKE_ARTWORK); } demoFilter.addAction(ACTION_DEMO); context.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter, android.Manifest.permission.DUMP, null); // listen for USER_SETUP_COMPLETE setting (per-user) resetUserSetupObserver(); // disable profiling bars, since they overlap and clutter the output on app windows ThreadedRenderer.overrideProperty("disableProfileBars", "true"); // Private API call to make the shadows look better for Recents ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f)); return mStatusBarView; } private void clearAllNotifications() { // animate-swipe all dismissable notifications, then animate the shade closed int numChildren = mStackScroller.getChildCount(); final ArrayList<View> viewsToHide = new ArrayList<View>(numChildren); for (int i = 0; i < numChildren; i++) { final View child = mStackScroller.getChildAt(i); if (child instanceof ExpandableNotificationRow) { if (mStackScroller.canChildBeDismissed(child)) { if (child.getVisibility() == View.VISIBLE) { viewsToHide.add(child); } } ExpandableNotificationRow row = (ExpandableNotificationRow) child; List<ExpandableNotificationRow> children = row.getNotificationChildren(); if (row.areChildrenExpanded() && children != null) { for (ExpandableNotificationRow childRow : children) { if (childRow.getVisibility() == View.VISIBLE) { viewsToHide.add(childRow); } } } } } if (viewsToHide.isEmpty()) { animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); return; } addPostCollapseAction(new Runnable() { @Override public void run() { mStackScroller.setDismissAllInProgress(false); try { mBarService.onClearAllNotifications(mCurrentUserId); } catch (Exception ex) { } } }); performDismissAllAnimations(viewsToHide); } private void performDismissAllAnimations(ArrayList<View> hideAnimatedList) { Runnable animationFinishAction = new Runnable() { @Override public void run() { animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); } }; // let's disable our normal animations mStackScroller.setDismissAllInProgress(true); // Decrease the delay for every row we animate to give the sense of // accelerating the swipes int rowDelayDecrement = 10; int currentDelay = 140; int totalDelay = 180; int numItems = hideAnimatedList.size(); for (int i = numItems - 1; i >= 0; i--) { View view = hideAnimatedList.get(i); Runnable endRunnable = null; if (i == 0) { endRunnable = animationFinishAction; } mStackScroller.dismissViewAnimated(view, endRunnable, totalDelay, 260); currentDelay = Math.max(50, currentDelay - rowDelayDecrement); totalDelay += currentDelay; } } @Override protected void setZenMode(int mode) { super.setZenMode(mode); if (mIconPolicy != null) { mIconPolicy.setZenMode(mode); } } private void startKeyguard() { KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class); mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this, mStatusBarWindow, mStatusBarWindowManager, mScrimController); mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback(); } @Override protected View getStatusBarView() { return mStatusBarView; } public StatusBarWindowView getStatusBarWindow() { return mStatusBarWindow; } public int getStatusBarHeight() { if (mNaturalBarHeight < 0) { final Resources res = mContext.getResources(); mNaturalBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); } return mNaturalBarHeight; } private View.OnClickListener mRecentsClickListener = new View.OnClickListener() { public void onClick(View v) { awakenDreams(); toggleRecentApps(); } }; private long mLastLockToAppLongPress; private View.OnLongClickListener mLongPressBackRecentsListener = new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { handleLongPressBackRecents(v); return true; } }; private final View.OnLongClickListener mLongPressHomeListener = new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { if (shouldDisableNavbarGestures()) { return false; } MetricsLogger.action(mContext, MetricsLogger.ACTION_ASSIST_LONG_PRESS); mAssistManager.startAssist(new Bundle() /* args */); awakenDreams(); if (mNavigationBarView != null) { mNavigationBarView.abortCurrentGesture(); } return true; } }; private final View.OnTouchListener mHomeActionListener = new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: awakenDreams(); break; } return false; } }; private void awakenDreams() { if (mDreamManager != null) { try { mDreamManager.awaken(); } catch (RemoteException e) { // fine, stay asleep then } } } private void prepareNavigationBarView() { mNavigationBarView.reorient(); mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener); mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener); mNavigationBarView.getRecentsButton().setLongClickable(true); mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener); mNavigationBarView.getBackButton().setLongClickable(true); mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener); mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener); mNavigationBarView.getHomeButton().setOnLongClickListener(mLongPressHomeListener); mAssistManager.onConfigurationChanged(); } // For small-screen devices (read: phones) that lack hardware navigation buttons private void addNavigationBar() { if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView); if (mNavigationBarView == null) return; prepareNavigationBarView(); mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams()); } private void repositionNavigationBar() { if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return; prepareNavigationBarView(); mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams()); } private void notifyNavigationBarScreenOn(boolean screenOn) { if (mNavigationBarView == null) return; mNavigationBarView.notifyScreenOn(screenOn); } private WindowManager.LayoutParams getNavigationBarLayoutParams() { WindowManager.LayoutParams lp = new WindowManager.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, 0 | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, PixelFormat.TRANSLUCENT); // this will allow the navbar to run in an overlay on devices that support this if (ActivityManager.isHighEndGfx()) { lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; } lp.setTitle("NavigationBar"); lp.windowAnimations = 0; return lp; } public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) { mIconController.addSystemIcon(slot, index, viewIndex, icon); } public void updateIcon(String slot, int index, int viewIndex, StatusBarIcon old, StatusBarIcon icon) { mIconController.updateSystemIcon(slot, index, viewIndex, old, icon); } public void removeIcon(String slot, int index, int viewIndex) { mIconController.removeSystemIcon(slot, index, viewIndex); } public UserHandle getCurrentUserHandle() { return new UserHandle(mCurrentUserId); } @Override public void addNotification(StatusBarNotification notification, RankingMap ranking, Entry oldEntry) { if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey()); Entry shadeEntry = createNotificationViews(notification); if (shadeEntry == null) { return; } boolean isHeadsUped = mUseHeadsUp && shouldInterrupt(shadeEntry); if (isHeadsUped) { mHeadsUpManager.showNotification(shadeEntry); // Mark as seen immediately setNotificationShown(notification); } if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) { // Stop screensaver if the notification has a full-screen intent. // (like an incoming phone call) awakenDreams(); // not immersive & a full-screen alert should be shown if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent"); try { EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, notification.getKey()); notification.getNotification().fullScreenIntent.send(); shadeEntry.notifyFullScreenIntentLaunched(); MetricsLogger.count(mContext, "note_fullscreen", 1); } catch (PendingIntent.CanceledException e) { } } addNotificationViews(shadeEntry, ranking); // Recalculate the position of the sliding windows and the titles. setAreThereNotifications(); } @Override protected void updateNotificationRanking(RankingMap ranking) { mNotificationData.updateRanking(ranking); updateNotifications(); } @Override public void removeNotification(String key, RankingMap ranking) { boolean deferRemoval = false; if (mHeadsUpManager.isHeadsUp(key)) { deferRemoval = !mHeadsUpManager.removeNotification(key); } if (key.equals(mMediaNotificationKey)) { clearCurrentMediaNotification(); updateMediaMetaData(true); } if (deferRemoval) { mLatestRankingMap = ranking; mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key)); return; } StatusBarNotification old = removeNotificationViews(key, ranking); if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old); if (old != null) { if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications() && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) { if (mState == StatusBarState.SHADE) { animateCollapsePanels(); } else if (mState == StatusBarState.SHADE_LOCKED) { goToKeyguard(); } } } setAreThereNotifications(); } @Override protected void refreshLayout(int layoutDirection) { if (mNavigationBarView != null) { mNavigationBarView.setLayoutDirection(layoutDirection); } } private void updateNotificationShade() { if (mStackScroller == null) return; // Do not modify the notifications during collapse. if (isCollapsing()) { addPostCollapseAction(new Runnable() { @Override public void run() { updateNotificationShade(); } }); return; } ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size()); final int N = activeNotifications.size(); for (int i=0; i<N; i++) { Entry ent = activeNotifications.get(i); int vis = ent.notification.getNotification().visibility; // Display public version of the notification if we need to redact. final boolean hideSensitive = !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId()); boolean sensitiveNote = vis == Notification.VISIBILITY_PRIVATE; boolean sensitivePackage = packageHasVisibilityOverride(ent.notification.getKey()); boolean sensitive = (sensitiveNote && hideSensitive) || sensitivePackage; boolean showingPublic = sensitive && isLockscreenPublicMode(); ent.row.setSensitive(sensitive); if (ent.autoRedacted && ent.legacy) { // TODO: Also fade this? Or, maybe easier (and better), provide a dark redacted form // for legacy auto redacted notifications. if (showingPublic) { ent.row.setShowingLegacyBackground(false); } else { ent.row.setShowingLegacyBackground(true); } } if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) { ExpandableNotificationRow summary = mGroupManager.getGroupSummary( ent.row.getStatusBarNotification()); List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(summary); if (orderedChildren == null) { orderedChildren = new ArrayList<>(); mTmpChildOrderMap.put(summary, orderedChildren); } orderedChildren.add(ent.row); } else { toShow.add(ent.row); } } ArrayList<View> toRemove = new ArrayList<>(); for (int i=0; i< mStackScroller.getChildCount(); i++) { View child = mStackScroller.getChildAt(i); if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) { toRemove.add(child); } } for (View remove : toRemove) { mStackScroller.removeView(remove); } for (int i=0; i<toShow.size(); i++) { View v = toShow.get(i); if (v.getParent() == null) { mStackScroller.addView(v); } } // So after all this work notifications still aren't sorted correctly. // Let's do that now by advancing through toShow and mStackScroller in // lock-step, making sure mStackScroller matches what we see in toShow. int j = 0; for (int i = 0; i < mStackScroller.getChildCount(); i++) { View child = mStackScroller.getChildAt(i); if (!(child instanceof ExpandableNotificationRow)) { // We don't care about non-notification views. continue; } ExpandableNotificationRow targetChild = toShow.get(j); if (child != targetChild) { // Oops, wrong notification at this position. Put the right one // here and advance both lists. mStackScroller.changeViewPosition(targetChild, i); } j++; } // lets handle the child notifications now updateNotificationShadeForChildren(); // clear the map again for the next usage mTmpChildOrderMap.clear(); updateRowStates(); updateSpeedbump(); updateClearAll(); updateEmptyShadeView(); updateQsExpansionEnabled(); mShadeUpdates.check(); } /** * Disable QS if device not provisioned. * If the user switcher is simple then disable QS during setup because * the user intends to use the lock screen user switcher, QS in not needed. */ private void updateQsExpansionEnabled() { mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned() && (mUserSetup || mUserSwitcherController == null || !mUserSwitcherController.isSimpleUserSwitcher()) && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0) && !ONLY_CORE_APPS); } private void updateNotificationShadeForChildren() { ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>(); boolean orderChanged = false; for (int i = 0; i < mStackScroller.getChildCount(); i++) { View view = mStackScroller.getChildAt(i); if (!(view instanceof ExpandableNotificationRow)) { // We don't care about non-notification views. continue; } ExpandableNotificationRow parent = (ExpandableNotificationRow) view; List<ExpandableNotificationRow> children = parent.getNotificationChildren(); List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent); // lets first remove all undesired children if (children != null) { toRemove.clear(); for (ExpandableNotificationRow childRow : children) { if (orderedChildren == null || !orderedChildren.contains(childRow)) { toRemove.add(childRow); } } for (ExpandableNotificationRow remove : toRemove) { parent.removeChildNotification(remove); mStackScroller.notifyGroupChildRemoved(remove); } } // We now add all the children which are not in there already for (int childIndex = 0; orderedChildren != null && childIndex < orderedChildren.size(); childIndex++) { ExpandableNotificationRow childView = orderedChildren.get(childIndex); if (children == null || !children.contains(childView)) { parent.addChildNotification(childView, childIndex); mStackScroller.notifyGroupChildAdded(childView); } } // Finally after removing and adding has been beformed we can apply the order. orderChanged |= parent.applyChildOrder(orderedChildren); } if (orderChanged) { mStackScroller.generateChildOrderChangedEvent(); } } private boolean packageHasVisibilityOverride(String key) { return mNotificationData.getVisibilityOverride(key) != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE; } private void updateClearAll() { boolean showDismissView = mState != StatusBarState.KEYGUARD && mNotificationData.hasActiveClearableNotifications(); mStackScroller.updateDismissView(showDismissView); } private void updateEmptyShadeView() { boolean showEmptyShade = mState != StatusBarState.KEYGUARD && mNotificationData.getActiveNotifications().size() == 0; mNotificationPanel.setShadeEmpty(showEmptyShade); } private void updateSpeedbump() { int speedbumpIndex = -1; int currentIndex = 0; ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); final int N = activeNotifications.size(); for (int i = 0; i < N; i++) { Entry entry = activeNotifications.get(i); boolean isChild = !isTopLevelChild(entry); if (isChild) { continue; } if (entry.row.getVisibility() != View.GONE && mNotificationData.isAmbient(entry.key)) { speedbumpIndex = currentIndex; break; } currentIndex++; } mStackScroller.updateSpeedBumpIndex(speedbumpIndex); } public static boolean isTopLevelChild(Entry entry) { return entry.row.getParent() instanceof NotificationStackScrollLayout; } @Override protected void updateNotifications() { mNotificationData.filterAndSort(); updateNotificationShade(); mIconController.updateNotificationIcons(mNotificationData); } @Override protected void updateRowStates() { super.updateRowStates(); mNotificationPanel.notifyVisibleChildrenChanged(); } @Override protected void setAreThereNotifications() { if (SPEW) { final boolean clearable = hasActiveNotifications() && mNotificationData.hasActiveClearableNotifications(); Log.d(TAG, "setAreThereNotifications: N=" + mNotificationData.getActiveNotifications().size() + " any=" + hasActiveNotifications() + " clearable=" + clearable); } final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out); final boolean showDot = hasActiveNotifications() && !areLightsOn(); if (showDot != (nlo.getAlpha() == 1.0f)) { if (showDot) { nlo.setAlpha(0f); nlo.setVisibility(View.VISIBLE); } nlo.animate() .alpha(showDot?1:0) .setDuration(showDot?750:250) .setInterpolator(new AccelerateInterpolator(2.0f)) .setListener(showDot ? null : new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator _a) { nlo.setVisibility(View.GONE); } }) .start(); } findAndUpdateMediaNotifications(); } public void findAndUpdateMediaNotifications() { boolean metaDataChanged = false; synchronized (mNotificationData) { ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); final int N = activeNotifications.size(); // Promote the media notification with a controller in 'playing' state, if any. Entry mediaNotification = null; MediaController controller = null; for (int i = 0; i < N; i++) { final Entry entry = activeNotifications.get(i); if (isMediaNotification(entry)) { final MediaSession.Token token = entry.notification.getNotification().extras .getParcelable(Notification.EXTRA_MEDIA_SESSION); if (token != null) { MediaController aController = new MediaController(mContext, token); if (PlaybackState.STATE_PLAYING == getMediaControllerPlaybackState(aController)) { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching " + entry.notification.getKey()); } mediaNotification = entry; controller = aController; break; } } } } if (mediaNotification == null) { // Still nothing? OK, let's just look for live media sessions and see if they match // one of our notifications. This will catch apps that aren't (yet!) using media // notifications. if (mMediaSessionManager != null) { final List<MediaController> sessions = mMediaSessionManager.getActiveSessionsForUser( null, UserHandle.USER_ALL); for (MediaController aController : sessions) { if (PlaybackState.STATE_PLAYING == getMediaControllerPlaybackState(aController)) { // now to see if we have one like this final String pkg = aController.getPackageName(); for (int i = 0; i < N; i++) { final Entry entry = activeNotifications.get(i); if (entry.notification.getPackageName().equals(pkg)) { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: found controller matching " + entry.notification.getKey()); } controller = aController; mediaNotification = entry; break; } } } } } } if (controller != null && !sameSessions(mMediaController, controller)) { // We have a new media session clearCurrentMediaNotification(); mMediaController = controller; mMediaController.registerCallback(mMediaListener); mMediaMetadata = mMediaController.getMetadata(); if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: " + mMediaMetadata); } if (mediaNotification != null) { mMediaNotificationKey = mediaNotification.notification.getKey(); if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key=" + mMediaNotificationKey + " controller=" + mMediaController); } } metaDataChanged = true; } } if (metaDataChanged) { updateNotifications(); } updateMediaMetaData(metaDataChanged); } private int getMediaControllerPlaybackState(MediaController controller) { if (controller != null) { final PlaybackState playbackState = controller.getPlaybackState(); if (playbackState != null) { return playbackState.getState(); } } return PlaybackState.STATE_NONE; } private boolean isPlaybackActive(int state) { if (state != PlaybackState.STATE_STOPPED && state != PlaybackState.STATE_ERROR && state != PlaybackState.STATE_NONE) { return true; } return false; } private void clearCurrentMediaNotification() { mMediaNotificationKey = null; mMediaMetadata = null; if (mMediaController != null) { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: " + mMediaController.getPackageName()); } mMediaController.unregisterCallback(mMediaListener); } mMediaController = null; } private boolean sameSessions(MediaController a, MediaController b) { if (a == b) return true; if (a == null) return false; return a.controlsSameSession(b); } /** * Hide the album artwork that is fading out and release its bitmap. */ private Runnable mHideBackdropFront = new Runnable() { @Override public void run() { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: removing fade layer"); } mBackdropFront.setVisibility(View.INVISIBLE); mBackdropFront.animate().cancel(); mBackdropFront.setImageDrawable(null); } }; /** * Refresh or remove lockscreen artwork from media metadata. */ public void updateMediaMetaData(boolean metaDataChanged) { if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) return; if (mBackdrop == null) return; // called too early if (mLaunchTransitionFadingAway) { mBackdrop.setVisibility(View.INVISIBLE); return; } if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + mMediaNotificationKey + " metadata=" + mMediaMetadata + " metaDataChanged=" + metaDataChanged + " state=" + mState); } Bitmap artworkBitmap = null; if (mMediaMetadata != null) { artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART); if (artworkBitmap == null) { artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); // might still be null } } final boolean hasArtwork = artworkBitmap != null; if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) { // time to show some art! if (mBackdrop.getVisibility() != View.VISIBLE) { mBackdrop.setVisibility(View.VISIBLE); mBackdrop.animate().alpha(1f); metaDataChanged = true; if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork"); } } if (metaDataChanged) { if (mBackdropBack.getDrawable() != null) { Drawable drawable = mBackdropBack.getDrawable().getConstantState().newDrawable().mutate(); mBackdropFront.setImageDrawable(drawable); if (mScrimSrcModeEnabled) { mBackdropFront.getDrawable().mutate().setXfermode(mSrcOverXferMode); } mBackdropFront.setAlpha(1f); mBackdropFront.setVisibility(View.VISIBLE); } else { mBackdropFront.setVisibility(View.INVISIBLE); } if (DEBUG_MEDIA_FAKE_ARTWORK) { final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF); Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c)); mBackdropBack.setBackgroundColor(0xFFFFFFFF); mBackdropBack.setImageDrawable(new ColorDrawable(c)); } else { mBackdropBack.setImageBitmap(artworkBitmap); } if (mScrimSrcModeEnabled) { mBackdropBack.getDrawable().mutate().setXfermode(mSrcXferMode); } if (mBackdropFront.getVisibility() == View.VISIBLE) { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from " + mBackdropFront.getDrawable() + " to " + mBackdropBack.getDrawable()); } mBackdropFront.animate() .setDuration(250) .alpha(0f).withEndAction(mHideBackdropFront); } } } else { // need to hide the album art, either because we are unlocked or because // the metadata isn't there to support it if (mBackdrop.getVisibility() != View.GONE) { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork"); } mBackdrop.animate() // Never let the alpha become zero - otherwise the RenderNode // won't draw anything and uninitialized memory will show through // if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in libhwui. .alpha(0.002f) .setInterpolator(mBackdropInterpolator) .setDuration(300) .setStartDelay(0) .withEndAction(new Runnable() { @Override public void run() { mBackdrop.setVisibility(View.GONE); mBackdropFront.animate().cancel(); mBackdropBack.animate().cancel(); mHandler.post(mHideBackdropFront); } }); if (mKeyguardFadingAway) { mBackdrop.animate() // Make it disappear faster, as the focus should be on the activity behind. .setDuration(mKeyguardFadingAwayDuration / 2) .setStartDelay(mKeyguardFadingAwayDelay) .setInterpolator(mLinearInterpolator) .start(); } } } } private int adjustDisableFlags(int state) { if (!mLaunchTransitionFadingAway && !mKeyguardFadingAway && (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit)) { state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS; state |= StatusBarManager.DISABLE_SYSTEM_INFO; } return state; } /** * State is one or more of the DISABLE constants from StatusBarManager. */ public void disable(int state1, int state2, boolean animate) { animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN; mDisabledUnmodified1 = state1; mDisabledUnmodified2 = state2; state1 = adjustDisableFlags(state1); final int old1 = mDisabled1; final int diff1 = state1 ^ old1; mDisabled1 = state1; final int old2 = mDisabled2; final int diff2 = state2 ^ old2; mDisabled2 = state2; if (DEBUG) { Log.d(TAG, String.format("disable1: 0x%08x -> 0x%08x (diff1: 0x%08x)", old1, state1, diff1)); Log.d(TAG, String.format("disable2: 0x%08x -> 0x%08x (diff2: 0x%08x)", old2, state2, diff2)); } StringBuilder flagdbg = new StringBuilder(); flagdbg.append("disable: < "); flagdbg.append(((state1 & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand"); flagdbg.append(((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " "); flagdbg.append(((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons"); flagdbg.append(((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " "); flagdbg.append(((state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts"); flagdbg.append(((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " "); flagdbg.append(((state1 & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info"); flagdbg.append(((diff1 & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " "); flagdbg.append(((state1 & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back"); flagdbg.append(((diff1 & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " "); flagdbg.append(((state1 & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home"); flagdbg.append(((diff1 & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " "); flagdbg.append(((state1 & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent"); flagdbg.append(((diff1 & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " "); flagdbg.append(((state1 & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock"); flagdbg.append(((diff1 & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " "); flagdbg.append(((state1 & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search"); flagdbg.append(((diff1 & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " "); flagdbg.append(((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) ? "QUICK_SETTINGS" : "quick_settings"); flagdbg.append(((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) ? "* " : " "); flagdbg.append(">"); Log.d(TAG, flagdbg.toString()); if ((diff1 & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) { if ((state1 & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) { mIconController.hideSystemIconArea(animate); } else { mIconController.showSystemIconArea(animate); } } if ((diff1 & StatusBarManager.DISABLE_CLOCK) != 0) { boolean visible = (state1 & StatusBarManager.DISABLE_CLOCK) == 0; mIconController.setClockVisibility(visible); } if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) { if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) { animateCollapsePanels(); } } if ((diff1 & (StatusBarManager.DISABLE_HOME | StatusBarManager.DISABLE_RECENT | StatusBarManager.DISABLE_BACK | StatusBarManager.DISABLE_SEARCH)) != 0) { // the nav bar will take care of these if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state1); if ((state1 & StatusBarManager.DISABLE_RECENT) != 0) { // close recents if it's visible mHandler.removeMessages(MSG_HIDE_RECENT_APPS); mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS); } } if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { if ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { mIconController.hideNotificationIconArea(animate); } else { mIconController.showNotificationIconArea(animate); } } if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) { mDisableNotificationAlerts = (state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; mHeadsUpObserver.onChange(true); } if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) { updateQsExpansionEnabled(); } } @Override protected BaseStatusBar.H createHandler() { return new PhoneStatusBar.H(); } @Override public void startActivity(Intent intent, boolean dismissShade) { startActivityDismissingKeyguard(intent, false, dismissShade); } @Override public void startActivity(Intent intent, boolean dismissShade, Callback callback) { startActivityDismissingKeyguard(intent, false, dismissShade, callback); } @Override public void preventNextAnimation() { overrideActivityPendingAppTransition(true /* keyguardShowing */); } public void setQsExpanded(boolean expanded) { mStatusBarWindowManager.setQsExpanded(expanded); mKeyguardStatusView.setImportantForAccessibility(expanded ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); } public boolean isGoingToNotificationShade() { return mLeaveOpenOnKeyguardHide; } public boolean isQsExpanded() { return mNotificationPanel.isQsExpanded(); } public boolean isScreenOnComingFromTouch() { return mScreenOnComingFromTouch; } public boolean isFalsingThresholdNeeded() { return getBarState() == StatusBarState.KEYGUARD; } public boolean isDozing() { return mDozing; } @Override // NotificationData.Environment public String getCurrentMediaNotificationKey() { return mMediaNotificationKey; } public boolean isScrimSrcModeEnabled() { return mScrimSrcModeEnabled; } /** * To be called when there's a state change in StatusBarKeyguardViewManager. */ public void onKeyguardViewManagerStatesUpdated() { logStateToEventlog(); } @Override // UnlockMethodCache.OnUnlockMethodChangedListener public void onUnlockMethodStateChanged() { logStateToEventlog(); } @Override public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { if (inPinnedMode) { mStatusBarWindowManager.setHeadsUpShowing(true); mStatusBarWindowManager.setForceStatusBarVisible(true); if (mNotificationPanel.isFullyCollapsed()) { // We need to ensure that the touchable region is updated before the window will be // resized, in order to not catch any touches. A layout will ensure that // onComputeInternalInsets will be called and after that we can resize the layout. Let's // make sure that the window stays small for one frame until the touchableRegion is set. mNotificationPanel.requestLayout(); mStatusBarWindowManager.setForceWindowCollapsed(true); mNotificationPanel.post(new Runnable() { @Override public void run() { mStatusBarWindowManager.setForceWindowCollapsed(false); } }); } } else { if (!mNotificationPanel.isFullyCollapsed() || mNotificationPanel.isTracking()) { // We are currently tracking or is open and the shade doesn't need to be kept // open artificially. mStatusBarWindowManager.setHeadsUpShowing(false); } else { // we need to keep the panel open artificially, let's wait until the animation // is finished. mHeadsUpManager.setHeadsUpGoingAway(true); mStackScroller.runAfterAnimationFinished(new Runnable() { @Override public void run() { if (!mHeadsUpManager.hasPinnedHeadsUp()) { mStatusBarWindowManager.setHeadsUpShowing(false); mHeadsUpManager.setHeadsUpGoingAway(false); } } }); } } } @Override public void onHeadsUpPinned(ExpandableNotificationRow headsUp) { dismissVolumeDialog(); } @Override public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) { } @Override public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) { if (!isHeadsUp && mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) { removeNotification(entry.key, mLatestRankingMap); mHeadsUpEntriesToRemoveOnSwitch.remove(entry); if (mHeadsUpEntriesToRemoveOnSwitch.isEmpty()) { mLatestRankingMap = null; } } else { updateNotificationRanking(null); } } protected void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt, boolean alertAgain) { final boolean wasHeadsUp = isHeadsUp(key); if (wasHeadsUp) { if (!shouldInterrupt) { // We don't want this to be interrupting anymore, lets remove it mHeadsUpManager.removeNotification(key); } else { mHeadsUpManager.updateNotification(entry, alertAgain); } } else if (shouldInterrupt && alertAgain) { // This notification was updated to be a heads-up, show it! mHeadsUpManager.showNotification(entry); } } protected void setHeadsUpUser(int newUserId) { if (mHeadsUpManager != null) { mHeadsUpManager.setUser(newUserId); } } public boolean isHeadsUp(String key) { return mHeadsUpManager.isHeadsUp(key); } protected boolean isSnoozedPackage(StatusBarNotification sbn) { return mHeadsUpManager.isSnoozed(sbn.getPackageName()); } public boolean isKeyguardCurrentlySecure() { return !mUnlockMethodCache.canSkipBouncer(); } public void setPanelExpanded(boolean isExpanded) { mStatusBarWindowManager.setPanelExpanded(isExpanded); } /** * All changes to the status bar and notifications funnel through here and are batched. */ private class H extends BaseStatusBar.H { public void handleMessage(Message m) { super.handleMessage(m); switch (m.what) { case MSG_OPEN_NOTIFICATION_PANEL: animateExpandNotificationsPanel(); break; case MSG_OPEN_SETTINGS_PANEL: animateExpandSettingsPanel(); break; case MSG_CLOSE_PANELS: animateCollapsePanels(); break; case MSG_LAUNCH_TRANSITION_TIMEOUT: onLaunchTransitionTimeout(); break; } } } @Override public void maybeEscalateHeadsUp() { TreeSet<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getSortedEntries(); for (HeadsUpManager.HeadsUpEntry entry : entries) { final StatusBarNotification sbn = entry.entry.notification; final Notification notification = sbn.getNotification(); if (notification.fullScreenIntent != null) { if (DEBUG) { Log.d(TAG, "converting a heads up to fullScreen"); } try { EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION, sbn.getKey()); notification.fullScreenIntent.send(); entry.entry.notifyFullScreenIntentLaunched(); } catch (PendingIntent.CanceledException e) { } } } mHeadsUpManager.releaseAllImmediately(); } boolean panelsEnabled() { return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0 && !ONLY_CORE_APPS; } void makeExpandedVisible(boolean force) { if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); if (!force && (mExpandedVisible || !panelsEnabled())) { return; } mExpandedVisible = true; if (mNavigationBarView != null) mNavigationBarView.setSlippery(true); // Expand the window to encompass the full screen in anticipation of the drag. // This is only possible to do atomically because the status bar is at the top of the screen! mStatusBarWindowManager.setPanelVisible(true); visibilityChanged(true); mWaitingForKeyguardExit = false; disable(mDisabledUnmodified1, mDisabledUnmodified2, !force /* animate */); setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); } public void animateCollapsePanels() { animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); } private final Runnable mAnimateCollapsePanels = new Runnable() { @Override public void run() { animateCollapsePanels(); } }; public void postAnimateCollapsePanels() { mHandler.post(mAnimateCollapsePanels); } public void animateCollapsePanels(int flags) { animateCollapsePanels(flags, false /* force */, false /* delayed */, 1.0f /* speedUpFactor */); } public void animateCollapsePanels(int flags, boolean force) { animateCollapsePanels(flags, force, false /* delayed */, 1.0f /* speedUpFactor */); } public void animateCollapsePanels(int flags, boolean force, boolean delayed) { animateCollapsePanels(flags, force, delayed, 1.0f /* speedUpFactor */); } public void animateCollapsePanels(int flags, boolean force, boolean delayed, float speedUpFactor) { if (!force && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) { runPostCollapseRunnables(); return; } if (SPEW) { Log.d(TAG, "animateCollapse():" + " mExpandedVisible=" + mExpandedVisible + " flags=" + flags); } if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) { if (!mHandler.hasMessages(MSG_HIDE_RECENT_APPS)) { mHandler.removeMessages(MSG_HIDE_RECENT_APPS); mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS); } } if (mStatusBarWindow != null) { // release focus immediately to kick off focus change transition mStatusBarWindowManager.setStatusBarFocusable(false); mStatusBarWindow.cancelExpandHelper(); mStatusBarView.collapseAllPanels(true /* animate */, delayed, speedUpFactor); } } private void runPostCollapseRunnables() { ArrayList<Runnable> clonedList = new ArrayList<>(mPostCollapseRunnables); mPostCollapseRunnables.clear(); int size = clonedList.size(); for (int i = 0; i < size; i++) { clonedList.get(i).run(); } } Animator mScrollViewAnim, mClearButtonAnim; @Override public void animateExpandNotificationsPanel() { if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); if (!panelsEnabled()) { return ; } mNotificationPanel.expand(); if (false) postStartTracing(); } @Override public void animateExpandSettingsPanel() { if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); if (!panelsEnabled()) { return; } // Settings are not available in setup if (!mUserSetup) return; mNotificationPanel.expandWithQs(); if (false) postStartTracing(); } public void animateCollapseQuickSettings() { if (mState == StatusBarState.SHADE) { mStatusBarView.collapseAllPanels(true, false /* delayed */, 1.0f /* speedUpFactor */); } } void makeExpandedInvisible() { if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible + " mExpandedVisible=" + mExpandedVisible); if (!mExpandedVisible || mStatusBarWindow == null) { return; } // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868) mStatusBarView.collapseAllPanels(/*animate=*/ false, false /* delayed*/, 1.0f /* speedUpFactor */); mNotificationPanel.closeQs(); mExpandedVisible = false; if (mNavigationBarView != null) mNavigationBarView.setSlippery(false); visibilityChanged(false); // Shrink the window to the size of the status bar only mStatusBarWindowManager.setPanelVisible(false); mStatusBarWindowManager.setForceStatusBarVisible(false); // Close any "App info" popups that might have snuck on-screen dismissPopups(); runPostCollapseRunnables(); setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); showBouncer(); disable(mDisabledUnmodified1, mDisabledUnmodified2, true /* animate */); // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in // the bouncer appear animation. if (!mStatusBarKeyguardViewManager.isShowing()) { WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); } } public boolean interceptTouchEvent(MotionEvent event) { if (DEBUG_GESTURES) { if (event.getActionMasked() != MotionEvent.ACTION_MOVE) { EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH, event.getActionMasked(), (int) event.getX(), (int) event.getY(), mDisabled1, mDisabled2); } } if (SPEW) { Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled1=" + mDisabled1 + " mDisabled2=" + mDisabled2 + " mTracking=" + mTracking); } else if (CHATTY) { if (event.getAction() != MotionEvent.ACTION_MOVE) { Log.d(TAG, String.format( "panel: %s at (%f, %f) mDisabled1=0x%08x mDisabled2=0x%08x", MotionEvent.actionToString(event.getAction()), event.getRawX(), event.getRawY(), mDisabled1, mDisabled2)); } } if (DEBUG_GESTURES) { mGestureRec.add(event); } if (mStatusBarWindowState == WINDOW_STATE_SHOWING) { final boolean upOrCancel = event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL; if (upOrCancel && !mExpandedVisible) { setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); } else { setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); } } return false; } public GestureRecorder getGestureRecorder() { return mGestureRec; } private void setNavigationIconHints(int hints) { if (hints == mNavigationIconHints) return; mNavigationIconHints = hints; if (mNavigationBarView != null) { mNavigationBarView.setNavigationIconHints(hints); } checkBarModes(); } @Override // CommandQueue public void setWindowState(int window, int state) { boolean showing = state == WINDOW_STATE_SHOWING; if (mStatusBarWindow != null && window == StatusBarManager.WINDOW_STATUS_BAR && mStatusBarWindowState != state) { mStatusBarWindowState = state; if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state)); if (!showing && mState == StatusBarState.SHADE) { mStatusBarView.collapseAllPanels(false /* animate */, false /* delayed */, 1.0f /* speedUpFactor */); } } if (mNavigationBarView != null && window == StatusBarManager.WINDOW_NAVIGATION_BAR && mNavigationBarWindowState != state) { mNavigationBarWindowState = state; if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state)); } } @Override // CommandQueue public void buzzBeepBlinked() { if (mDozeServiceHost != null) { mDozeServiceHost.fireBuzzBeepBlinked(); } } @Override public void notificationLightOff() { if (mDozeServiceHost != null) { mDozeServiceHost.fireNotificationLight(false); } } @Override public void notificationLightPulse(int argb, int onMillis, int offMillis) { if (mDozeServiceHost != null) { mDozeServiceHost.fireNotificationLight(true); } } @Override // CommandQueue public void setSystemUiVisibility(int vis, int mask) { final int oldVal = mSystemUiVisibility; final int newVal = (oldVal&~mask) | (vis&mask); final int diff = newVal ^ oldVal; if (DEBUG) Log.d(TAG, String.format( "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s", Integer.toHexString(vis), Integer.toHexString(mask), Integer.toHexString(oldVal), Integer.toHexString(newVal), Integer.toHexString(diff))); if (diff != 0) { // we never set the recents bit via this method, so save the prior state to prevent // clobbering the bit below final boolean wasRecentsVisible = (mSystemUiVisibility & View.RECENT_APPS_VISIBLE) > 0; mSystemUiVisibility = newVal; // update low profile if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) { final boolean lightsOut = (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0; if (lightsOut) { animateCollapsePanels(); } setAreThereNotifications(); } // ready to unhide if ((vis & View.STATUS_BAR_UNHIDE) != 0) { mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE; mNoAnimationOnNextBarModeChange = true; } // update status bar mode final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(), View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT); // update navigation bar mode final int nbMode = mNavigationBarView == null ? -1 : computeBarMode( oldVal, newVal, mNavigationBarView.getBarTransitions(), View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT); final boolean sbModeChanged = sbMode != -1; final boolean nbModeChanged = nbMode != -1; boolean checkBarModes = false; if (sbModeChanged && sbMode != mStatusBarMode) { mStatusBarMode = sbMode; checkBarModes = true; } if (nbModeChanged && nbMode != mNavigationBarMode) { mNavigationBarMode = nbMode; checkBarModes = true; } if (checkBarModes) { checkBarModes(); } if (sbModeChanged || nbModeChanged) { // update transient bar autohide if (mStatusBarMode == MODE_SEMI_TRANSPARENT || mNavigationBarMode == MODE_SEMI_TRANSPARENT) { scheduleAutohide(); } else { cancelAutohide(); } } if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) { mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE; } if ((diff & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0 || sbModeChanged) { boolean isTransparentBar = (mStatusBarMode == MODE_TRANSPARENT || mStatusBarMode == MODE_LIGHTS_OUT_TRANSPARENT); boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave(); boolean light = (vis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0; mIconController.setIconsDark(allowLight && light); } // restore the recents bit if (wasRecentsVisible) { mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; } // send updated sysui visibility to window manager notifyUiVisibilityChanged(mSystemUiVisibility); } } private int computeBarMode(int oldVis, int newVis, BarTransitions transitions, int transientFlag, int translucentFlag) { final int oldMode = barMode(oldVis, transientFlag, translucentFlag); final int newMode = barMode(newVis, transientFlag, translucentFlag); if (oldMode == newMode) { return -1; // no mode change } return newMode; } private int barMode(int vis, int transientFlag, int translucentFlag) { int lightsOutTransparent = View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_TRANSPARENT; return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT : (vis & lightsOutTransparent) == lightsOutTransparent ? MODE_LIGHTS_OUT_TRANSPARENT : (vis & View.SYSTEM_UI_TRANSPARENT) != 0 ? MODE_TRANSPARENT : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT : MODE_OPAQUE; } private void checkBarModes() { if (mDemoMode) return; checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions(), mNoAnimationOnNextBarModeChange); if (mNavigationBarView != null) { checkBarMode(mNavigationBarMode, mNavigationBarWindowState, mNavigationBarView.getBarTransitions(), mNoAnimationOnNextBarModeChange); } mNoAnimationOnNextBarModeChange = false; } private void checkBarMode(int mode, int windowState, BarTransitions transitions, boolean noAnimation) { final boolean powerSave = mBatteryController.isPowerSave(); final boolean anim = !noAnimation && (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN && !powerSave; if (powerSave && getBarState() == StatusBarState.SHADE) { mode = MODE_WARNING; } transitions.transitionTo(mode, anim); } private void finishBarAnimations() { mStatusBarView.getBarTransitions().finishAnimations(); if (mNavigationBarView != null) { mNavigationBarView.getBarTransitions().finishAnimations(); } } private final Runnable mCheckBarModes = new Runnable() { @Override public void run() { checkBarModes(); } }; @Override public void setInteracting(int barWindow, boolean interacting) { final boolean changing = ((mInteractingWindows & barWindow) != 0) != interacting; mInteractingWindows = interacting ? (mInteractingWindows | barWindow) : (mInteractingWindows & ~barWindow); if (mInteractingWindows != 0) { suspendAutohide(); } else { resumeSuspendedAutohide(); } // manually dismiss the volume panel when interacting with the nav bar if (changing && interacting && barWindow == StatusBarManager.WINDOW_NAVIGATION_BAR) { dismissVolumeDialog(); } checkBarModes(); } private void dismissVolumeDialog() { if (mVolumeComponent != null) { mVolumeComponent.dismissNow(); } } private void resumeSuspendedAutohide() { if (mAutohideSuspended) { scheduleAutohide(); mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher } } private void suspendAutohide() { mHandler.removeCallbacks(mAutohide); mHandler.removeCallbacks(mCheckBarModes); mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0; } private void cancelAutohide() { mAutohideSuspended = false; mHandler.removeCallbacks(mAutohide); } private void scheduleAutohide() { cancelAutohide(); mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS); } private void checkUserAutohide(View v, MotionEvent event) { if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0 // a transient bar is revealed && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar && event.getX() == 0 && event.getY() == 0 // a touch outside both bars ) { userAutohide(); } } private void userAutohide() { cancelAutohide(); mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear } private boolean areLightsOn() { return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE); } public void setLightsOn(boolean on) { Log.v(TAG, "setLightsOn(" + on + ")"); if (on) { setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE); } else { setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE); } } private void notifyUiVisibilityChanged(int vis) { try { if (mLastDispatchedSystemUiVisibility != vis) { mWindowManagerService.statusBarVisibilityChanged(vis); mLastDispatchedSystemUiVisibility = vis; } } catch (RemoteException ex) { } } public void topAppWindowChanged(boolean showMenu) { if (DEBUG) { Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button"); } if (mNavigationBarView != null) { mNavigationBarView.setMenuVisibility(showMenu); } // See above re: lights-out policy for legacy apps. if (showMenu) setLightsOn(true); } @Override public void setImeWindowStatus(IBinder token, int vis, int backDisposition, boolean showImeSwitcher) { boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0; int flags = mNavigationIconHints; if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) { flags |= NAVIGATION_HINT_BACK_ALT; } else { flags &= ~NAVIGATION_HINT_BACK_ALT; } if (showImeSwitcher) { flags |= NAVIGATION_HINT_IME_SHOWN; } else { flags &= ~NAVIGATION_HINT_IME_SHOWN; } setNavigationIconHints(flags); } public static String viewInfo(View v) { return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom() + ") " + v.getWidth() + "x" + v.getHeight() + "]"; } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { synchronized (mQueueLock) { pw.println("Current Status Bar state:"); pw.println(" mExpandedVisible=" + mExpandedVisible + ", mTrackingPosition=" + mTrackingPosition); pw.println(" mTracking=" + mTracking); pw.println(" mDisplayMetrics=" + mDisplayMetrics); pw.println(" mStackScroller: " + viewInfo(mStackScroller)); pw.println(" mStackScroller: " + viewInfo(mStackScroller) + " scroll " + mStackScroller.getScrollX() + "," + mStackScroller.getScrollY()); } pw.print(" mInteractingWindows="); pw.println(mInteractingWindows); pw.print(" mStatusBarWindowState="); pw.println(windowStateToString(mStatusBarWindowState)); pw.print(" mStatusBarMode="); pw.println(BarTransitions.modeToString(mStatusBarMode)); pw.print(" mDozing="); pw.println(mDozing); pw.print(" mZenMode="); pw.println(Settings.Global.zenModeToString(mZenMode)); pw.print(" mUseHeadsUp="); pw.println(mUseHeadsUp); dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions()); if (mNavigationBarView != null) { pw.print(" mNavigationBarWindowState="); pw.println(windowStateToString(mNavigationBarWindowState)); pw.print(" mNavigationBarMode="); pw.println(BarTransitions.modeToString(mNavigationBarMode)); dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions()); } pw.print(" mNavigationBarView="); if (mNavigationBarView == null) { pw.println("null"); } else { mNavigationBarView.dump(fd, pw, args); } pw.print(" mMediaSessionManager="); pw.println(mMediaSessionManager); pw.print(" mMediaNotificationKey="); pw.println(mMediaNotificationKey); pw.print(" mMediaController="); pw.print(mMediaController); if (mMediaController != null) { pw.print(" state=" + mMediaController.getPlaybackState()); } pw.println(); pw.print(" mMediaMetadata="); pw.print(mMediaMetadata); if (mMediaMetadata != null) { pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE)); } pw.println(); pw.println(" Panels: "); if (mNotificationPanel != null) { pw.println(" mNotificationPanel=" + mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug("")); pw.print (" "); mNotificationPanel.dump(fd, pw, args); } DozeLog.dump(pw); if (DUMPTRUCK) { synchronized (mNotificationData) { mNotificationData.dump(pw, " "); } mIconController.dump(pw); if (false) { pw.println("see the logcat for a dump of the views we have created."); // must happen on ui thread mHandler.post(new Runnable() { public void run() { mStatusBarView.getLocationOnScreen(mAbsPos); Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] + ") " + mStatusBarView.getWidth() + "x" + getStatusBarHeight()); mStatusBarView.debug(); } }); } } if (DEBUG_GESTURES) { pw.print(" status bar gestures: "); mGestureRec.dump(fd, pw, args); } if (mStatusBarWindowManager != null) { mStatusBarWindowManager.dump(fd, pw, args); } if (mNetworkController != null) { mNetworkController.dump(fd, pw, args); } if (mBluetoothController != null) { mBluetoothController.dump(fd, pw, args); } if (mHotspotController != null) { mHotspotController.dump(fd, pw, args); } if (mCastController != null) { mCastController.dump(fd, pw, args); } if (mUserSwitcherController != null) { mUserSwitcherController.dump(fd, pw, args); } if (mBatteryController != null) { mBatteryController.dump(fd, pw, args); } if (mNextAlarmController != null) { mNextAlarmController.dump(fd, pw, args); } if (mAssistManager != null) { mAssistManager.dump(fd, pw, args); } if (mSecurityController != null) { mSecurityController.dump(fd, pw, args); } if (mHeadsUpManager != null) { mHeadsUpManager.dump(fd, pw, args); } else { pw.println(" mHeadsUpManager: null"); } if (KeyguardUpdateMonitor.getInstance(mContext) != null) { KeyguardUpdateMonitor.getInstance(mContext).dump(fd, pw, args); } pw.println("SharedPreferences:"); for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) { pw.print(" "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue()); } } private String hunStateToString(Entry entry) { if (entry == null) return "null"; if (entry.notification == null) return "corrupt"; return entry.notification.getPackageName(); } private static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) { pw.print(" "); pw.print(var); pw.print(".BarTransitions.mMode="); pw.println(BarTransitions.modeToString(transitions.getMode())); } @Override public void createAndAddWindows() { addStatusBarWindow(); } private void addStatusBarWindow() { makeStatusBarView(); mStatusBarWindowManager = new StatusBarWindowManager(mContext); mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); } // called by makeStatusbar and also by PhoneStatusBarView void updateDisplaySize() { mDisplay.getMetrics(mDisplayMetrics); mDisplay.getSize(mCurrentDisplaySize); if (DEBUG_GESTURES) { mGestureRec.tag("display", String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)); } } float getDisplayDensity() { return mDisplayMetrics.density; } public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned, boolean dismissShade) { startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, null /* callback */); } public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned, final boolean dismissShade, final Callback callback) { if (onlyProvisioned && !isDeviceProvisioned()) return; final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity( mContext, intent, mCurrentUserId); final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); Runnable runnable = new Runnable() { public void run() { mAssistManager.hideAssist(); intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); int result = ActivityManager.START_CANCELED; try { result = ActivityManagerNative.getDefault().startActivityAsUser( null, mContext.getBasePackageName(), intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null, UserHandle.CURRENT.getIdentifier()); } catch (RemoteException e) { Log.w(TAG, "Unable to start activity", e); } overrideActivityPendingAppTransition( keyguardShowing && !afterKeyguardGone); if (callback != null) { callback.onActivityStarted(result); } } }; Runnable cancelRunnable = new Runnable() { @Override public void run() { if (callback != null) { callback.onActivityStarted(ActivityManager.START_CANCELED); } } }; executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShade, afterKeyguardGone); } public void executeRunnableDismissingKeyguard(final Runnable runnable, final Runnable cancelAction, final boolean dismissShade, final boolean afterKeyguardGone) { final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); dismissKeyguardThenExecute(new OnDismissAction() { @Override public boolean onDismiss() { AsyncTask.execute(new Runnable() { public void run() { try { if (keyguardShowing && !afterKeyguardGone) { ActivityManagerNative.getDefault() .keyguardWaitingForActivityDrawn(); } if (runnable != null) { runnable.run(); } } catch (RemoteException e) { } } }); if (dismissShade) { animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */, true /* delayed*/); } return true; } }, cancelAction, afterKeyguardGone); } private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { if (DEBUG) Log.v(TAG, "onReceive: " + intent); String action = intent.getAction(); if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { if (isCurrentProfile(getSendingUserId())) { int flags = CommandQueue.FLAG_EXCLUDE_NONE; String reason = intent.getStringExtra("reason"); if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) { flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL; } animateCollapsePanels(flags); } } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { mScreenOn = false; notifyNavigationBarScreenOn(false); notifyHeadsUpScreenOff(); finishBarAnimations(); resetUserExpandedStates(); } else if (Intent.ACTION_SCREEN_ON.equals(action)) { mScreenOn = true; notifyNavigationBarScreenOn(true); } } }; private BroadcastReceiver mDemoReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { if (DEBUG) Log.v(TAG, "onReceive: " + intent); String action = intent.getAction(); if (ACTION_DEMO.equals(action)) { Bundle bundle = intent.getExtras(); if (bundle != null) { String command = bundle.getString("command", "").trim().toLowerCase(); if (command.length() > 0) { try { dispatchDemoCommand(command, bundle); } catch (Throwable t) { Log.w(TAG, "Error running demo command, intent=" + intent, t); } } } } else if (ACTION_FAKE_ARTWORK.equals(action)) { if (DEBUG_MEDIA_FAKE_ARTWORK) { updateMediaMetaData(true); } } } }; private void resetUserExpandedStates() { ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); final int notificationCount = activeNotifications.size(); for (int i = 0; i < notificationCount; i++) { NotificationData.Entry entry = activeNotifications.get(i); if (entry.row != null) { entry.row.resetUserExpansion(); } } } @Override protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) { dismissKeyguardThenExecute(action, null /* cancelRunnable */, afterKeyguardGone); } private void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction, boolean afterKeyguardGone) { if (mStatusBarKeyguardViewManager.isShowing()) { mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction, afterKeyguardGone); } else { action.onDismiss(); } } // SystemUIService notifies SystemBars of configuration changes, which then calls down here @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // calls refreshLayout if (DEBUG) { Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration()); } updateDisplaySize(); // populates mDisplayMetrics updateResources(); repositionNavigationBar(); updateRowStates(); mIconController.updateResources(); mScreenPinningRequest.onConfigurationChanged(); mNetworkController.onConfigurationChanged(); } @Override public void userSwitched(int newUserId) { super.userSwitched(newUserId); if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId); animateCollapsePanels(); updatePublicMode(); updateNotifications(); resetUserSetupObserver(); setControllerUsers(); mAssistManager.onUserSwitched(newUserId); } private void setControllerUsers() { if (mZenModeController != null) { mZenModeController.setUserId(mCurrentUserId); } if (mSecurityController != null) { mSecurityController.onUserSwitched(mCurrentUserId); } } private void resetUserSetupObserver() { mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver); mUserSetupObserver.onChange(false); mContext.getContentResolver().registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true, mUserSetupObserver, mCurrentUserId); } /** * Reload some of our resources when the configuration changes. * * We don't reload everything when the configuration changes -- we probably * should, but getting that smooth is tough. Someday we'll fix that. In the * meantime, just update the things that we know change. */ void updateResources() { // Update the quick setting tiles if (mQSPanel != null) { mQSPanel.updateResources(); } loadDimens(); if (mNotificationPanel != null) { mNotificationPanel.updateResources(); } if (mBrightnessMirrorController != null) { mBrightnessMirrorController.updateResources(); } } protected void loadDimens() { final Resources res = mContext.getResources(); mNaturalBarHeight = res.getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height); mRowMinHeight = res.getDimensionPixelSize(R.dimen.notification_min_height); mRowMaxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height); mKeyguardMaxNotificationCount = res.getInteger(R.integer.keyguard_max_notification_count); if (DEBUG) Log.v(TAG, "updateResources"); } // Visibility reporting @Override protected void handleVisibleToUserChanged(boolean visibleToUser) { if (visibleToUser) { super.handleVisibleToUserChanged(visibleToUser); startNotificationLogging(); } else { stopNotificationLogging(); super.handleVisibleToUserChanged(visibleToUser); } } private void stopNotificationLogging() { // Report all notifications as invisible and turn down the // reporter. if (!mCurrentlyVisibleNotifications.isEmpty()) { logNotificationVisibilityChanges(Collections.<NotificationVisibility>emptyList(), mCurrentlyVisibleNotifications); recycleAllVisibilityObjects(mCurrentlyVisibleNotifications); } mHandler.removeCallbacks(mVisibilityReporter); mStackScroller.setChildLocationsChangedListener(null); } private void startNotificationLogging() { mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener); // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't // cause the scroller to emit child location events. Hence generate // one ourselves to guarantee that we're reporting visible // notifications. // (Note that in cases where the scroller does emit events, this // additional event doesn't break anything.) mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller); } private void logNotificationVisibilityChanges( Collection<NotificationVisibility> newlyVisible, Collection<NotificationVisibility> noLongerVisible) { if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) { return; } NotificationVisibility[] newlyVisibleAr = newlyVisible.toArray(new NotificationVisibility[newlyVisible.size()]); NotificationVisibility[] noLongerVisibleAr = noLongerVisible.toArray(new NotificationVisibility[noLongerVisible.size()]); try { mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr); } catch (RemoteException e) { // Ignore. } final int N = newlyVisible.size(); if (N > 0) { String[] newlyVisibleKeyAr = new String[N]; for (int i = 0; i < N; i++) { newlyVisibleKeyAr[i] = newlyVisibleAr[i].key; } setNotificationsShown(newlyVisibleKeyAr); } } // State logging private void logStateToEventlog() { boolean isShowing = mStatusBarKeyguardViewManager.isShowing(); boolean isOccluded = mStatusBarKeyguardViewManager.isOccluded(); boolean isBouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing(); boolean isSecure = mUnlockMethodCache.isMethodSecure(); boolean canSkipBouncer = mUnlockMethodCache.canSkipBouncer(); int stateFingerprint = getLoggingFingerprint(mState, isShowing, isOccluded, isBouncerShowing, isSecure, canSkipBouncer); if (stateFingerprint != mLastLoggedStateFingerprint) { EventLogTags.writeSysuiStatusBarState(mState, isShowing ? 1 : 0, isOccluded ? 1 : 0, isBouncerShowing ? 1 : 0, isSecure ? 1 : 0, canSkipBouncer ? 1 : 0); mLastLoggedStateFingerprint = stateFingerprint; } } /** * Returns a fingerprint of fields logged to eventlog */ private static int getLoggingFingerprint(int statusBarState, boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing, boolean secure, boolean currentlyInsecure) { // Reserve 8 bits for statusBarState. We'll never go higher than // that, right? Riiiight. return (statusBarState & 0xFF) | ((keyguardShowing ? 1 : 0) << 8) | ((keyguardOccluded ? 1 : 0) << 9) | ((bouncerShowing ? 1 : 0) << 10) | ((secure ? 1 : 0) << 11) | ((currentlyInsecure ? 1 : 0) << 12); } // // tracing // void postStartTracing() { mHandler.postDelayed(mStartTracing, 3000); } void vibrate() { android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService( Context.VIBRATOR_SERVICE); vib.vibrate(250, VIBRATION_ATTRIBUTES); } Runnable mStartTracing = new Runnable() { public void run() { vibrate(); SystemClock.sleep(250); Log.d(TAG, "startTracing"); android.os.Debug.startMethodTracing("/data/statusbar-traces/trace"); mHandler.postDelayed(mStopTracing, 10000); } }; Runnable mStopTracing = new Runnable() { public void run() { android.os.Debug.stopMethodTracing(); Log.d(TAG, "stopTracing"); vibrate(); } }; @Override public boolean shouldDisableNavbarGestures() { return !isDeviceProvisioned() || (mDisabled1 & StatusBarManager.DISABLE_SEARCH) != 0; } public void postStartActivityDismissingKeyguard(final Intent intent, int delay) { mHandler.postDelayed(new Runnable() { @Override public void run() { handleStartActivityDismissingKeyguard(intent, true /*onlyProvisioned*/); } }, delay); } private void handleStartActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned) { startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */); } private static class FastColorDrawable extends Drawable { private final int mColor; public FastColorDrawable(int color) { mColor = 0xff000000 | color; } @Override public void draw(Canvas canvas) { canvas.drawColor(mColor, PorterDuff.Mode.SRC); } @Override public void setAlpha(int alpha) { } @Override public void setColorFilter(ColorFilter colorFilter) { } @Override public int getOpacity() { return PixelFormat.OPAQUE; } @Override public void setBounds(int left, int top, int right, int bottom) { } @Override public void setBounds(Rect bounds) { } } @Override public void destroy() { super.destroy(); if (mStatusBarWindow != null) { mWindowManager.removeViewImmediate(mStatusBarWindow); mStatusBarWindow = null; } if (mNavigationBarView != null) { mWindowManager.removeViewImmediate(mNavigationBarView); mNavigationBarView = null; } if (mHandlerThread != null) { mHandlerThread.quitSafely(); mHandlerThread = null; } mContext.unregisterReceiver(mBroadcastReceiver); mContext.unregisterReceiver(mDemoReceiver); mAssistManager.destroy(); final SignalClusterView signalCluster = (SignalClusterView) mStatusBarView.findViewById(R.id.signal_cluster); final SignalClusterView signalClusterKeyguard = (SignalClusterView) mKeyguardStatusBar.findViewById(R.id.signal_cluster); final SignalClusterView signalClusterQs = (SignalClusterView) mHeader.findViewById(R.id.signal_cluster); mNetworkController.removeSignalCallback(signalCluster); mNetworkController.removeSignalCallback(signalClusterKeyguard); mNetworkController.removeSignalCallback(signalClusterQs); if (mQSPanel != null && mQSPanel.getHost() != null) { mQSPanel.getHost().destroy(); } } private boolean mDemoModeAllowed; private boolean mDemoMode; @Override public void dispatchDemoCommand(String command, Bundle args) { if (!mDemoModeAllowed) { mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(), DEMO_MODE_ALLOWED, 0) != 0; } if (!mDemoModeAllowed) return; if (command.equals(COMMAND_ENTER)) { mDemoMode = true; } else if (command.equals(COMMAND_EXIT)) { mDemoMode = false; checkBarModes(); } else if (!mDemoMode) { // automatically enter demo mode on first demo command dispatchDemoCommand(COMMAND_ENTER, new Bundle()); } boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT); if ((modeChange || command.equals(COMMAND_VOLUME)) && mVolumeComponent != null) { mVolumeComponent.dispatchDemoCommand(command, args); } if (modeChange || command.equals(COMMAND_CLOCK)) { dispatchDemoCommandToView(command, args, R.id.clock); } if (modeChange || command.equals(COMMAND_BATTERY)) { dispatchDemoCommandToView(command, args, R.id.battery); } if (modeChange || command.equals(COMMAND_STATUS)) { mIconController.dispatchDemoCommand(command, args); } if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) { mNetworkController.dispatchDemoCommand(command, args); } if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) { View notifications = mStatusBarView == null ? null : mStatusBarView.findViewById(R.id.notification_icon_area); if (notifications != null) { String visible = args.getString("visible"); int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE; notifications.setVisibility(vis); } } if (command.equals(COMMAND_BARS)) { String mode = args.getString("mode"); int barMode = "opaque".equals(mode) ? MODE_OPAQUE : "translucent".equals(mode) ? MODE_TRANSLUCENT : "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT : "transparent".equals(mode) ? MODE_TRANSPARENT : "warning".equals(mode) ? MODE_WARNING : -1; if (barMode != -1) { boolean animate = true; if (mStatusBarView != null) { mStatusBarView.getBarTransitions().transitionTo(barMode, animate); } if (mNavigationBarView != null) { mNavigationBarView.getBarTransitions().transitionTo(barMode, animate); } } } } private void dispatchDemoCommandToView(String command, Bundle args, int id) { if (mStatusBarView == null) return; View v = mStatusBarView.findViewById(id); if (v instanceof DemoMode) { ((DemoMode)v).dispatchDemoCommand(command, args); } } /** * @return The {@link StatusBarState} the status bar is in. */ public int getBarState() { return mState; } @Override protected boolean isPanelFullyCollapsed() { return mNotificationPanel.isFullyCollapsed(); } public void showKeyguard() { if (mLaunchTransitionFadingAway) { mNotificationPanel.animate().cancel(); onLaunchTransitionFadingEnded(); } mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); setBarState(StatusBarState.KEYGUARD); updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */); if (!mScreenOnFromKeyguard) { // If the screen is off already, we need to disable touch events because these might // collapse the panel after we expanded it, and thus we would end up with a blank // Keyguard. mNotificationPanel.setTouchDisabled(true); } instantExpandNotificationsPanel(); mLeaveOpenOnKeyguardHide = false; if (mDraggedDownRow != null) { mDraggedDownRow.setUserLocked(false); mDraggedDownRow.notifyHeightChanged(false /* needsAnimation */); mDraggedDownRow = null; } mAssistManager.onLockscreenShown(); } private void onLaunchTransitionFadingEnded() { mNotificationPanel.setAlpha(1.0f); runLaunchTransitionEndRunnable(); mLaunchTransitionFadingAway = false; mScrimController.forceHideScrims(false /* hide */); updateMediaMetaData(true /* metaDataChanged */); } public boolean isCollapsing() { return mNotificationPanel.isCollapsing(); } public void addPostCollapseAction(Runnable r) { mPostCollapseRunnables.add(r); } public boolean isInLaunchTransition() { return mNotificationPanel.isLaunchTransitionRunning() || mNotificationPanel.isLaunchTransitionFinished(); } /** * Fades the content of the keyguard away after the launch transition is done. * * @param beforeFading the runnable to be run when the circle is fully expanded and the fading * starts * @param endRunnable the runnable to be run when the transition is done */ public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading, Runnable endRunnable) { mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); mLaunchTransitionEndRunnable = endRunnable; Runnable hideRunnable = new Runnable() { @Override public void run() { mLaunchTransitionFadingAway = true; if (beforeFading != null) { beforeFading.run(); } mScrimController.forceHideScrims(true /* hide */); updateMediaMetaData(false); mNotificationPanel.setAlpha(1); mNotificationPanel.animate() .alpha(0) .setStartDelay(FADE_KEYGUARD_START_DELAY) .setDuration(FADE_KEYGUARD_DURATION) .withLayer() .withEndAction(new Runnable() { @Override public void run() { onLaunchTransitionFadingEnded(); } }); mIconController.appTransitionStarting(SystemClock.uptimeMillis(), StatusBarIconController.DEFAULT_TINT_ANIMATION_DURATION); } }; if (mNotificationPanel.isLaunchTransitionRunning()) { mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable); } else { hideRunnable.run(); } } /** * Starts the timeout when we try to start the affordances on Keyguard. We usually rely that * Keyguard goes away via fadeKeyguardAfterLaunchTransition, however, that might not happen * because the launched app crashed or something else went wrong. */ public void startLaunchTransitionTimeout() { mHandler.sendEmptyMessageDelayed(MSG_LAUNCH_TRANSITION_TIMEOUT, LAUNCH_TRANSITION_TIMEOUT_MS); } private void onLaunchTransitionTimeout() { Log.w(TAG, "Launch transition: Timeout!"); mNotificationPanel.resetViews(); } private void runLaunchTransitionEndRunnable() { if (mLaunchTransitionEndRunnable != null) { Runnable r = mLaunchTransitionEndRunnable; // mLaunchTransitionEndRunnable might call showKeyguard, which would execute it again, // which would lead to infinite recursion. Protect against it. mLaunchTransitionEndRunnable = null; r.run(); } } /** * @return true if we would like to stay in the shade, false if it should go away entirely */ public boolean hideKeyguard() { boolean staying = mLeaveOpenOnKeyguardHide; setBarState(StatusBarState.SHADE); if (mLeaveOpenOnKeyguardHide) { mLeaveOpenOnKeyguardHide = false; mNotificationPanel.animateToFullShade(calculateGoingToFullShadeDelay()); if (mDraggedDownRow != null) { mDraggedDownRow.setUserLocked(false); mDraggedDownRow = null; } } else { instantCollapseNotificationPanel(); } updateKeyguardState(staying, false /* fromShadeLocked */); // Keyguard state has changed, but QS is not listening anymore. Make sure to update the tile // visibilities so next time we open the panel we know the correct height already. if (mQSPanel != null) { mQSPanel.refreshAllTiles(); } mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); return staying; } public long calculateGoingToFullShadeDelay() { return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration; } /** * Notifies the status bar that Keyguard is going away very soon. */ public void keyguardGoingAway() { // Treat Keyguard exit animation as an app transition to achieve nice transition for status // bar. mIconController.appTransitionPending(); } /** * Notifies the status bar the Keyguard is fading away with the specified timings. * * @param startTime the start time of the animations in uptime millis * @param delay the precalculated animation delay in miliseconds * @param fadeoutDuration the duration of the exit animation, in milliseconds */ public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration) { mKeyguardFadingAway = true; mKeyguardFadingAwayDelay = delay; mKeyguardFadingAwayDuration = fadeoutDuration; mWaitingForKeyguardExit = false; mIconController.appTransitionStarting( startTime + fadeoutDuration - StatusBarIconController.DEFAULT_TINT_ANIMATION_DURATION, StatusBarIconController.DEFAULT_TINT_ANIMATION_DURATION); disable(mDisabledUnmodified1, mDisabledUnmodified2, fadeoutDuration > 0 /* animate */); } public boolean isKeyguardFadingAway() { return mKeyguardFadingAway; } /** * Notifies that the Keyguard fading away animation is done. */ public void finishKeyguardFadingAway() { mKeyguardFadingAway = false; } public void stopWaitingForKeyguardExit() { mWaitingForKeyguardExit = false; } private void updatePublicMode() { setLockscreenPublicMode( mStatusBarKeyguardViewManager.isShowing() && mStatusBarKeyguardViewManager .isSecure(mCurrentUserId)); } private void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) { if (mState == StatusBarState.KEYGUARD) { mKeyguardIndicationController.setVisible(true); mNotificationPanel.resetViews(); mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked); mStatusBarView.removePendingHideExpandedRunnables(); } else { mKeyguardIndicationController.setVisible(false); mKeyguardUserSwitcher.setKeyguard(false, goingToFullShade || mState == StatusBarState.SHADE_LOCKED || fromShadeLocked); } if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { mScrimController.setKeyguardShowing(true); mIconPolicy.setKeyguardShowing(true); } else { mScrimController.setKeyguardShowing(false); mIconPolicy.setKeyguardShowing(false); } mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade); updateDozingState(); updatePublicMode(); updateStackScrollerState(goingToFullShade); updateNotifications(); checkBarModes(); updateMediaMetaData(false); mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(), mStatusBarKeyguardViewManager.isSecure()); } private void updateDozingState() { boolean animate = !mDozing && mDozeScrimController.isPulsing(); mNotificationPanel.setDozing(mDozing, animate); mStackScroller.setDark(mDozing, animate, mScreenOnTouchLocation); mScrimController.setDozing(mDozing); mDozeScrimController.setDozing(mDozing, animate); } public void updateStackScrollerState(boolean goingToFullShade) { if (mStackScroller == null) return; boolean onKeyguard = mState == StatusBarState.KEYGUARD; mStackScroller.setHideSensitive(isLockscreenPublicMode(), goingToFullShade); mStackScroller.setDimmed(onKeyguard, false /* animate */); mStackScroller.setExpandingEnabled(!onKeyguard); ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild(); mStackScroller.setActivatedChild(null); if (activatedChild != null) { activatedChild.makeInactive(false /* animate */); } } public void userActivity() { if (mState == StatusBarState.KEYGUARD) { mKeyguardViewMediatorCallback.userActivity(); } } public boolean interceptMediaKey(KeyEvent event) { return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.interceptMediaKey(event); } public boolean onMenuPressed() { return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.onMenuPressed(); } public boolean onBackPressed() { if (mStatusBarKeyguardViewManager.onBackPressed()) { return true; } if (mNotificationPanel.isQsExpanded()) { if (mNotificationPanel.isQsDetailShowing()) { mNotificationPanel.closeQsDetail(); } else { mNotificationPanel.animateCloseQs(); } return true; } if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) { animateCollapsePanels(); return true; } return false; } public boolean onSpacePressed() { if (mScreenOn != null && mScreenOn && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) { animateCollapsePanels( CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */); return true; } return false; } private void showBouncer() { if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing(); mStatusBarKeyguardViewManager.dismiss(); } } private void instantExpandNotificationsPanel() { // Make our window larger and the panel expanded. makeExpandedVisible(true); mNotificationPanel.instantExpand(); } private void instantCollapseNotificationPanel() { mNotificationPanel.instantCollapse(); } @Override public void onActivated(ActivatableNotificationView view) { EventLogTags.writeSysuiLockscreenGesture( EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_TAP_NOTIFICATION_ACTIVATE, 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */); mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again); ActivatableNotificationView previousView = mStackScroller.getActivatedChild(); if (previousView != null) { previousView.makeInactive(true /* animate */); } mStackScroller.setActivatedChild(view); } /** * @param state The {@link StatusBarState} to set. */ public void setBarState(int state) { // If we're visible and switched to SHADE_LOCKED (the user dragged // down on the lockscreen), clear notification LED, vibration, // ringing. // Other transitions are covered in handleVisibleToUserChanged(). if (state != mState && mVisible && state == StatusBarState.SHADE_LOCKED) { try { mBarService.clearNotificationEffects(); } catch (RemoteException e) { // Ignore. } } mState = state; mGroupManager.setStatusBarState(state); mStatusBarWindowManager.setStatusBarState(state); updateDozing(); } @Override public void onActivationReset(ActivatableNotificationView view) { if (view == mStackScroller.getActivatedChild()) { mKeyguardIndicationController.hideTransientIndication(); mStackScroller.setActivatedChild(null); } } public void onTrackingStarted() { runPostCollapseRunnables(); } public void onClosingFinished() { runPostCollapseRunnables(); } public void onUnlockHintStarted() { mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock); } public void onHintFinished() { // Delay the reset a bit so the user can read the text. mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS); } public void onCameraHintStarted() { mKeyguardIndicationController.showTransientIndication(R.string.camera_hint); } public void onVoiceAssistHintStarted() { mKeyguardIndicationController.showTransientIndication(R.string.voice_hint); } public void onPhoneHintStarted() { mKeyguardIndicationController.showTransientIndication(R.string.phone_hint); } public void onTrackingStopped(boolean expand) { if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { if (!expand && !mUnlockMethodCache.canSkipBouncer()) { showBouncer(); } } } @Override protected int getMaxKeyguardNotifications() { return mKeyguardMaxNotificationCount; } public NavigationBarView getNavigationBarView() { return mNavigationBarView; } // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------ @Override public boolean onDraggedDown(View startingChild, int dragLengthY) { if (hasActiveNotifications()) { EventLogTags.writeSysuiLockscreenGesture( EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_DOWN_FULL_SHADE, (int) (dragLengthY / mDisplayMetrics.density), 0 /* velocityDp - N/A */); // We have notifications, go to locked shade. goToLockedShade(startingChild); return true; } else { // No notifications - abort gesture. return false; } } @Override public void onDragDownReset() { mStackScroller.setDimmed(true /* dimmed */, true /* animated */); } @Override public void onThresholdReached() { mStackScroller.setDimmed(false /* dimmed */, true /* animate */); } @Override public void onTouchSlopExceeded() { mStackScroller.removeLongPressCallback(); } @Override public void setEmptyDragAmount(float amount) { mNotificationPanel.setEmptyDragAmount(amount); } /** * If secure with redaction: Show bouncer, go to unlocked shade. * * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p> * * @param expandView The view to expand after going to the shade. */ public void goToLockedShade(View expandView) { ExpandableNotificationRow row = null; if (expandView instanceof ExpandableNotificationRow) { row = (ExpandableNotificationRow) expandView; row.setUserExpanded(true); } boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId) || !mShowLockscreenNotifications; if (isLockscreenPublicMode() && fullShadeNeedsBouncer) { mLeaveOpenOnKeyguardHide = true; showBouncer(); mDraggedDownRow = row; } else { mNotificationPanel.animateToFullShade(0 /* delay */); setBarState(StatusBarState.SHADE_LOCKED); updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */); if (row != null) { row.setUserLocked(false); } } } /** * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}. */ public void goToKeyguard() { if (mState == StatusBarState.SHADE_LOCKED) { mStackScroller.onGoToKeyguard(); setBarState(StatusBarState.KEYGUARD); updateKeyguardState(false /* goingToFullShade */, true /* fromShadeLocked*/); } } public long getKeyguardFadingAwayDelay() { return mKeyguardFadingAwayDelay; } public long getKeyguardFadingAwayDuration() { return mKeyguardFadingAwayDuration; } @Override public void setBouncerShowing(boolean bouncerShowing) { super.setBouncerShowing(bouncerShowing); mStatusBarView.setBouncerShowing(bouncerShowing); disable(mDisabledUnmodified1, mDisabledUnmodified2, true /* animate */); } public void onScreenTurnedOff() { mScreenOnFromKeyguard = false; mScreenOnComingFromTouch = false; mScreenOnTouchLocation = null; mStackScroller.setAnimationsEnabled(false); updateVisibleToUser(); } public void onScreenTurnedOn() { mScreenOnFromKeyguard = true; mStackScroller.setAnimationsEnabled(true); mNotificationPanel.onScreenTurnedOn(); mNotificationPanel.setTouchDisabled(false); updateVisibleToUser(); } /** * This handles long-press of both back and recents. They are * handled together to capture them both being long-pressed * at the same time to exit screen pinning (lock task). * * When accessibility mode is on, only a long-press from recents * is required to exit. * * In all other circumstances we try to pass through long-press events * for Back, so that apps can still use it. Which can be from two things. * 1) Not currently in screen pinning (lock task). * 2) Back is long-pressed without recents. */ private void handleLongPressBackRecents(View v) { try { boolean sendBackLongPress = false; IActivityManager activityManager = ActivityManagerNative.getDefault(); boolean isAccessiblityEnabled = mAccessibilityManager.isEnabled(); if (activityManager.isInLockTaskMode() && !isAccessiblityEnabled) { long time = System.currentTimeMillis(); // If we recently long-pressed the other button then they were // long-pressed 'together' if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) { activityManager.stopLockTaskModeOnCurrent(); // When exiting refresh disabled flags. mNavigationBarView.setDisabledFlags(mDisabled1, true); } else if ((v.getId() == R.id.back) && !mNavigationBarView.getRecentsButton().isPressed()) { // If we aren't pressing recents right now then they presses // won't be together, so send the standard long-press action. sendBackLongPress = true; } mLastLockToAppLongPress = time; } else { // If this is back still need to handle sending the long-press event. if (v.getId() == R.id.back) { sendBackLongPress = true; } else if (isAccessiblityEnabled && activityManager.isInLockTaskMode()) { // When in accessibility mode a long press that is recents (not back) // should stop lock task. activityManager.stopLockTaskModeOnCurrent(); // When exiting refresh disabled flags. mNavigationBarView.setDisabledFlags(mDisabled1, true); } } if (sendBackLongPress) { KeyButtonView keyButtonView = (KeyButtonView) v; keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS); keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); } } catch (RemoteException e) { Log.d(TAG, "Unable to reach activity manager", e); } } // Recents @Override protected void showRecents(boolean triggeredFromAltTab) { // Set the recents visibility flag mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; notifyUiVisibilityChanged(mSystemUiVisibility); super.showRecents(triggeredFromAltTab); } @Override protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { // Unset the recents visibility flag mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE; notifyUiVisibilityChanged(mSystemUiVisibility); super.hideRecents(triggeredFromAltTab, triggeredFromHomeKey); } @Override protected void toggleRecents() { // Toggle the recents visibility flag mSystemUiVisibility ^= View.RECENT_APPS_VISIBLE; notifyUiVisibilityChanged(mSystemUiVisibility); super.toggleRecents(); } @Override public void onVisibilityChanged(boolean visible) { // Update the recents visibility flag if (visible) { mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; } else { mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE; } notifyUiVisibilityChanged(mSystemUiVisibility); } @Override public void showScreenPinningRequest() { if (mKeyguardMonitor.isShowing()) { // Don't allow apps to trigger this from keyguard. return; } // Show screen pinning request, since this comes from an app, show 'no thanks', button. showScreenPinningRequest(true); } public void showScreenPinningRequest(boolean allowCancel) { mScreenPinningRequest.showPrompt(allowCancel); } public boolean hasActiveNotifications() { return !mNotificationData.getActiveNotifications().isEmpty(); } public void wakeUpIfDozing(long time, MotionEvent event) { if (mDozing && mDozeScrimController.isPulsing()) { PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); pm.wakeUp(time, "com.android.systemui:NODOZE"); mScreenOnComingFromTouch = true; mScreenOnTouchLocation = new PointF(event.getX(), event.getY()); mNotificationPanel.setTouchDisabled(false); mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested(); } } @Override public void appTransitionPending() { // Use own timings when Keyguard is going away, see keyguardGoingAway and // setKeyguardFadingAway if (!mKeyguardFadingAway) { mIconController.appTransitionPending(); } } @Override public void appTransitionCancelled() { mIconController.appTransitionCancelled(); } @Override public void appTransitionStarting(long startTime, long duration) { // Use own timings when Keyguard is going away, see keyguardGoingAway and // setKeyguardFadingAway if (!mKeyguardFadingAway) { mIconController.appTransitionStarting(startTime, duration); } if (mIconPolicy != null) { mIconPolicy.appTransitionStarting(startTime, duration); } } private void updateDozing() { mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD; updateDozingState(); } private final class ShadeUpdates { private final ArraySet<String> mVisibleNotifications = new ArraySet<String>(); private final ArraySet<String> mNewVisibleNotifications = new ArraySet<String>(); public void check() { mNewVisibleNotifications.clear(); ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); for (int i = 0; i < activeNotifications.size(); i++) { final Entry entry = activeNotifications.get(i); final boolean visible = entry.row != null && entry.row.getVisibility() == View.VISIBLE; if (visible) { mNewVisibleNotifications.add(entry.key + entry.notification.getPostTime()); } } final boolean updates = !mVisibleNotifications.containsAll(mNewVisibleNotifications); mVisibleNotifications.clear(); mVisibleNotifications.addAll(mNewVisibleNotifications); // We have new notifications if (updates && mDozeServiceHost != null) { mDozeServiceHost.fireNewNotifications(); } } } private final class DozeServiceHost implements DozeHost { // Amount of time to allow to update the time shown on the screen before releasing // the wakelock. This timeout is design to compensate for the fact that we don't // currently have a way to know when time display contents have actually been // refreshed once we've finished rendering a new frame. private static final long PROCESSING_TIME = 500; private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); private final H mHandler = new H(); // Keeps the last reported state by fireNotificationLight. private boolean mNotificationLightOn; @Override public String toString() { return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]"; } public void firePowerSaveChanged(boolean active) { for (Callback callback : mCallbacks) { callback.onPowerSaveChanged(active); } } public void fireBuzzBeepBlinked() { for (Callback callback : mCallbacks) { callback.onBuzzBeepBlinked(); } } public void fireNotificationLight(boolean on) { mNotificationLightOn = on; for (Callback callback : mCallbacks) { callback.onNotificationLight(on); } } public void fireNewNotifications() { for (Callback callback : mCallbacks) { callback.onNewNotifications(); } } @Override public void addCallback(@NonNull Callback callback) { mCallbacks.add(callback); } @Override public void removeCallback(@NonNull Callback callback) { mCallbacks.remove(callback); } @Override public void startDozing(@NonNull Runnable ready) { mHandler.obtainMessage(H.MSG_START_DOZING, ready).sendToTarget(); } @Override public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) { mHandler.obtainMessage(H.MSG_PULSE_WHILE_DOZING, reason, 0, callback).sendToTarget(); } @Override public void stopDozing() { mHandler.obtainMessage(H.MSG_STOP_DOZING).sendToTarget(); } @Override public boolean isPowerSaveActive() { return mBatteryController != null && mBatteryController.isPowerSave(); } @Override public boolean isNotificationLightOn() { return mNotificationLightOn; } private void handleStartDozing(@NonNull Runnable ready) { if (!mDozingRequested) { mDozingRequested = true; DozeLog.traceDozing(mContext, mDozing); updateDozing(); } ready.run(); } private void handlePulseWhileDozing(@NonNull PulseCallback callback, int reason) { mDozeScrimController.pulse(callback, reason); } private void handleStopDozing() { if (mDozingRequested) { mDozingRequested = false; DozeLog.traceDozing(mContext, mDozing); updateDozing(); } } private final class H extends Handler { private static final int MSG_START_DOZING = 1; private static final int MSG_PULSE_WHILE_DOZING = 2; private static final int MSG_STOP_DOZING = 3; @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_START_DOZING: handleStartDozing((Runnable) msg.obj); break; case MSG_PULSE_WHILE_DOZING: handlePulseWhileDozing((PulseCallback) msg.obj, msg.arg1); break; case MSG_STOP_DOZING: handleStopDozing(); break; } } } } }