| /* |
| * 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.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.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.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.statusbar.StatusBarIcon; |
| import com.android.keyguard.KeyguardHostView.OnDismissAction; |
| 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.AssistGestureManager; |
| 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; |
| |
| 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; |
| |
| 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. |
| int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore |
| boolean mTracking; |
| VelocityTracker mVelocityTracker; |
| |
| int[] mAbsPos = new int[2]; |
| ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>(); |
| |
| // for disabling the status bar |
| int mDisabled = 0; |
| |
| // tracking calls to View.setSystemUiVisibility() |
| int mSystemUiVisibility = 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; |
| |
| private AssistGestureManager mAssistGestureManager; |
| |
| // 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(); |
| } |
| } |
| }; |
| |
| 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 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); |
| } |
| |
| @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 mDisabledUnmodified; |
| |
| /** Keys of notifications currently visible to the user. */ |
| private final ArraySet<String> mCurrentlyVisibleNotifications = new ArraySet<String>(); |
| 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_TOP_STACK_PEEKING |
| | StackViewState.LOCATION_MAIN_AREA |
| | StackViewState.LOCATION_BOTTOM_STACK_PEEKING; |
| |
| 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 ArrayList<String> mTmpNewlyVisibleNotifications = new ArrayList<String>(); |
| private final ArrayList<String> mTmpCurrentlyVisibleNotifications = new ArrayList<String>(); |
| |
| @Override |
| public void run() { |
| mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis(); |
| |
| // 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 previouslyVisible = mCurrentlyVisibleNotifications.contains(key); |
| boolean currentlyVisible = |
| (mStackScroller.getChildLocation(entry.row) & VISIBLE_LOCATIONS) != 0; |
| if (currentlyVisible) { |
| // Build new set of visible notifications. |
| mTmpCurrentlyVisibleNotifications.add(key); |
| } |
| if (!previouslyVisible && currentlyVisible) { |
| mTmpNewlyVisibleNotifications.add(key); |
| } |
| } |
| ArraySet<String> noLongerVisibleNotifications = mCurrentlyVisibleNotifications; |
| noLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications); |
| |
| logNotificationVisibilityChanges( |
| mTmpNewlyVisibleNotifications, noLongerVisibleNotifications); |
| |
| mCurrentlyVisibleNotifications.clear(); |
| mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications); |
| |
| mTmpNewlyVisibleNotifications.clear(); |
| mTmpCurrentlyVisibleNotifications.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; |
| |
| @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); |
| 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.mService = 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, mNotificationPanel.getViewTreeObserver()); |
| 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); |
| } |
| |
| updateShowSearchHoldoff(); |
| |
| 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(mDisabled); |
| mNavigationBarView.setBar(this); |
| mNavigationBarView.setOnVerticalChangedListener( |
| new NavigationBarView.OnVerticalChangedListener() { |
| @Override |
| public void onVerticalChanged(boolean isVertical) { |
| if (mAssistGestureManager != null) { |
| mAssistGestureManager.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 |
| } |
| |
| // 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.addView(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) { |
| 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); |
| mKeyguardIndicationController = new KeyguardIndicationController(mContext, |
| (KeyguardIndicationTextView) mStatusBarWindow.findViewById( |
| R.id.keyguard_indication_text)); |
| mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController); |
| |
| mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); |
| |
| // 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); // 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); |
| 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.addSignalCluster(signalCluster); |
| mNetworkController.addSignalCluster(signalClusterKeyguard); |
| mNetworkController.addSignalCluster(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(new NetworkControllerImpl.EmergencyListener() { |
| @Override |
| public void setEmergencyCallsOnly(boolean emergencyOnly) { |
| mHeader.setShowEmergencyCallsOnly(emergencyOnly); |
| } |
| }); |
| } |
| |
| mFlashlightController = new FlashlightController(mContext); |
| mKeyguardBottomArea.setFlashlightController(mFlashlightController); |
| mKeyguardBottomArea.setPhoneStatusBar(this); |
| mAccessibilityController = new AccessibilityController(mContext); |
| mKeyguardBottomArea.setAccessibilityController(mAccessibilityController); |
| mNextAlarmController = new NextAlarmController(mContext); |
| mKeyguardMonitor = new KeyguardMonitor(); |
| if (UserSwitcherController.isUserSwitcherAvailable(UserManager.get(mContext))) { |
| mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor); |
| } |
| 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); |
| 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)); |
| |
| mAssistGestureManager = new AssistGestureManager(this, context); |
| |
| // 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); |
| if (DEBUG_MEDIA_FAKE_ARTWORK) { |
| filter.addAction("fake_artwork"); |
| } |
| filter.addAction(ACTION_DEMO); |
| context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); |
| |
| // listen for USER_SETUP_COMPLETE setting (per-user) |
| resetUserSetupObserver(); |
| |
| 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() { |
| try { |
| mBarService.onClearAllNotifications(mCurrentUserId); |
| } catch (Exception ex) { } |
| } |
| }); |
| |
| performDismissAllAnimations(viewsToHide); |
| |
| } |
| |
| private void performDismissAllAnimations(ArrayList<View> hideAnimatedList) { |
| Runnable animationFinishAction = new Runnable() { |
| @Override |
| public void run() { |
| mStackScroller.post(new Runnable() { |
| @Override |
| public void run() { |
| mStackScroller.setDismissAllInProgress(false); |
| } |
| }); |
| 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 void invokeAssistGesture(boolean vibrate) { |
| mHandler.removeCallbacks(mInvokeAssist); |
| mAssistGestureManager.onGestureInvoked(vibrate); |
| } |
| |
| 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 int mShowSearchHoldoff = 0; |
| private Runnable mInvokeAssist = new Runnable() { |
| public void run() { |
| invokeAssistGesture(true /* vibrate */); |
| awakenDreams(); |
| if (mNavigationBarView != null) { |
| mNavigationBarView.getHomeButton().abortCurrentGesture(); |
| } |
| } |
| }; |
| |
| View.OnTouchListener mHomeActionListener = new View.OnTouchListener() { |
| public boolean onTouch(View v, MotionEvent event) { |
| switch (event.getAction()) { |
| case MotionEvent.ACTION_DOWN: |
| if (!shouldDisableNavbarGestures()) { |
| mHandler.removeCallbacks(mInvokeAssist); |
| mHandler.postDelayed(mInvokeAssist, mShowSearchHoldoff); |
| } |
| break; |
| |
| case MotionEvent.ACTION_UP: |
| case MotionEvent.ACTION_CANCEL: |
| mHandler.removeCallbacks(mInvokeAssist); |
| 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); |
| mAssistGestureManager.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(notification); |
| 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(); |
| } 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 defferRemoval = false; |
| if (mHeadsUpManager.isHeadsUp(key)) { |
| defferRemoval = !mHeadsUpManager.removeNotification(key); |
| } |
| if (defferRemoval) { |
| 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 updateShowSearchHoldoff() { |
| mShowSearchHoldoff = mContext.getResources().getInteger( |
| R.integer.config_show_search_delay); |
| } |
| |
| 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(); |
| |
| // 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. |
| mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned() |
| && (mUserSetup || mUserSwitcherController == null |
| || !mUserSwitcherController.isSimpleUserSwitcher())); |
| mShadeUpdates.check(); |
| } |
| |
| 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(); |
| 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) { |
| controller = new MediaController(mContext, token); |
| if (controller != null) { |
| // we've got a live one, here |
| mediaNotification = entry; |
| } |
| } |
| } |
| } |
| |
| 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 (aController == null) continue; |
| final PlaybackState state = aController.getPlaybackState(); |
| if (state == null) continue; |
| switch (state.getState()) { |
| case PlaybackState.STATE_STOPPED: |
| case PlaybackState.STATE_ERROR: |
| continue; |
| default: |
| // 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 (!sameSessions(mMediaController, controller)) { |
| // We have a new media session |
| |
| if (mMediaController != null) { |
| // something old was playing |
| Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: " |
| + mMediaController); |
| mMediaController.unregisterCallback(mMediaListener); |
| } |
| mMediaController = controller; |
| |
| if (mMediaController != null) { |
| mMediaController.registerCallback(mMediaListener); |
| mMediaMetadata = mMediaController.getMetadata(); |
| if (DEBUG_MEDIA) { |
| Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: " |
| + mMediaMetadata); |
| } |
| |
| final String notificationKey = mediaNotification == null |
| ? null |
| : mediaNotification.notification.getKey(); |
| |
| if (notificationKey == null || !notificationKey.equals(mMediaNotificationKey)) { |
| // we have a new notification! |
| if (DEBUG_MEDIA) { |
| Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key=" |
| + notificationKey + " controller=" + controller); |
| } |
| mMediaNotificationKey = notificationKey; |
| } |
| } else { |
| mMediaMetadata = null; |
| mMediaNotificationKey = null; |
| } |
| |
| metaDataChanged = true; |
| } else { |
| // Media session unchanged |
| |
| if (DEBUG_MEDIA) { |
| Log.v(TAG, "DEBUG_MEDIA: Continuing media notification: key=" + mMediaNotificationKey); |
| } |
| } |
| } |
| |
| updateMediaMetaData(metaDataChanged); |
| } |
| |
| 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 (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(); |
| 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() |
| .alpha(0f) |
| .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 |
| && (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 state, boolean animate) { |
| mDisabledUnmodified = state; |
| state = adjustDisableFlags(state); |
| final int old = mDisabled; |
| final int diff = state ^ old; |
| mDisabled = state; |
| |
| if (DEBUG) { |
| Log.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)", |
| old, state, diff)); |
| } |
| |
| StringBuilder flagdbg = new StringBuilder(); |
| flagdbg.append("disable: < "); |
| flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand"); |
| flagdbg.append(((diff & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " "); |
| flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons"); |
| flagdbg.append(((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " "); |
| flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts"); |
| flagdbg.append(((diff & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " "); |
| flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info"); |
| flagdbg.append(((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " "); |
| flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back"); |
| flagdbg.append(((diff & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " "); |
| flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home"); |
| flagdbg.append(((diff & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " "); |
| flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent"); |
| flagdbg.append(((diff & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " "); |
| flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock"); |
| flagdbg.append(((diff & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " "); |
| flagdbg.append(((state & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search"); |
| flagdbg.append(((diff & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " "); |
| flagdbg.append(">"); |
| Log.d(TAG, flagdbg.toString()); |
| |
| if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) { |
| if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) { |
| mIconController.hideSystemIconArea(animate); |
| } else { |
| mIconController.showSystemIconArea(animate); |
| } |
| } |
| |
| if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) { |
| boolean visible = (state & StatusBarManager.DISABLE_CLOCK) == 0; |
| mIconController.setClockVisibility(visible); |
| } |
| if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) { |
| if ((state & StatusBarManager.DISABLE_EXPAND) != 0) { |
| animateCollapsePanels(); |
| } |
| } |
| |
| if ((diff & (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(state); |
| |
| if ((state & StatusBarManager.DISABLE_RECENT) != 0) { |
| // close recents if it's visible |
| mHandler.removeMessages(MSG_HIDE_RECENT_APPS); |
| mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS); |
| } |
| } |
| |
| if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { |
| if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { |
| mIconController.hideNotificationIconArea(animate); |
| } else { |
| mIconController.showNotificationIconArea(animate); |
| } |
| } |
| |
| if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) { |
| mDisableNotificationAlerts = |
| (state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; |
| mHeadsUpObserver.onChange(true); |
| } |
| } |
| |
| @Override |
| protected BaseStatusBar.H createHandler() { |
| return new PhoneStatusBar.H(); |
| } |
| |
| @Override |
| public void startActivity(Intent intent, boolean dismissShade) { |
| startActivityDismissingKeyguard(intent, false, dismissShade); |
| } |
| |
| public void setQsExpanded(boolean expanded) { |
| mStatusBarWindowManager.setQsExpanded(expanded); |
| } |
| |
| public boolean isGoingToNotificationShade() { |
| return mLeaveOpenOnKeyguardHide; |
| } |
| |
| public boolean isQsExpanded() { |
| return mNotificationPanel.isQsExpanded(); |
| } |
| |
| public boolean isScreenOnComingFromTouch() { |
| return mScreenOnComingFromTouch; |
| } |
| |
| public boolean isFalsingThresholdNeeded() { |
| boolean onKeyguard = getBarState() == StatusBarState.KEYGUARD; |
| boolean isCurrentlyInsecure = mUnlockMethodCache.isCurrentlyInsecure(); |
| return onKeyguard && (isCurrentlyInsecure || mDozing || mScreenOnComingFromTouch); |
| } |
| |
| 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 OnPinnedHeadsUpExistChanged(boolean exist, boolean changeImmediatly) { |
| if (exist) { |
| mStatusBarWindowManager.setHeadsUpShowing(true); |
| } else { |
| Runnable endRunnable = new Runnable() { |
| @Override |
| public void run() { |
| if (!mHeadsUpManager.hasPinnedHeadsUp()) { |
| mStatusBarWindowManager.setHeadsUpShowing(false); |
| } |
| } |
| }; |
| if (changeImmediatly) { |
| endRunnable.run(); |
| } else { |
| mStackScroller.performOnAnimationFinished(endRunnable); |
| } |
| } |
| } |
| |
| @Override |
| public void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp) { |
| } |
| |
| @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) { |
| mHeadsUpManager.updateNotification(entry, alertAgain); |
| if (!shouldInterrupt) { |
| // We don't want this to be interrupting anymore, lets remove it |
| mHeadsUpManager.removeNotification(key); |
| } |
| } 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()); |
| } |
| |
| /** |
| * 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 escalateHeadsUp() { |
| 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(); |
| } catch (PendingIntent.CanceledException e) { |
| } |
| } |
| } |
| mHeadsUpManager.releaseAllImmediately(); |
| } |
| |
| boolean panelsEnabled() { |
| return (mDisabled & StatusBarManager.DISABLE_EXPAND) == 0; |
| } |
| |
| 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.setStatusBarExpanded(true); |
| mStatusBarView.setFocusable(false); |
| |
| visibilityChanged(true); |
| mWaitingForKeyguardExit = false; |
| disable(mDisabledUnmodified, !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() { |
| int size = mPostCollapseRunnables.size(); |
| for (int i = 0; i < size; i++) { |
| mPostCollapseRunnables.get(i).run(); |
| } |
| mPostCollapseRunnables.clear(); |
| } |
| |
| 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.setStatusBarExpanded(false); |
| mStatusBarView.setFocusable(true); |
| |
| // Close any "App info" popups that might have snuck on-screen |
| dismissPopups(); |
| |
| runPostCollapseRunnables(); |
| setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); |
| showBouncer(); |
| disable(mDisabledUnmodified, 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(), mDisabled); |
| } |
| |
| } |
| |
| if (SPEW) { |
| Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled=" |
| + mDisabled + " mTracking=" + mTracking); |
| } else if (CHATTY) { |
| if (event.getAction() != MotionEvent.ACTION_MOVE) { |
| Log.d(TAG, String.format( |
| "panel: %s at (%f, %f) mDisabled=0x%08x", |
| MotionEvent.actionToString(event.getAction()), |
| event.getRawX(), event.getRawY(), mDisabled)); |
| } |
| } |
| |
| 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(); |
| } |
| |
| // 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(); |
| } |
| } |
| |
| // ready to unhide |
| if ((vis & View.STATUS_BAR_UNHIDE) != 0) { |
| mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE; |
| } |
| 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()); |
| if (mNavigationBarView != null) { |
| checkBarMode(mNavigationBarMode, |
| mNavigationBarWindowState, mNavigationBarView.getBarTransitions()); |
| } |
| } |
| |
| private void checkBarMode(int mode, int windowState, BarTransitions transitions) { |
| final boolean powerSave = mBatteryController.isPowerSave(); |
| final boolean anim = (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) { |
| if (mVolumeComponent != null) { |
| mVolumeComponent.dismissNow(); |
| } |
| } |
| checkBarModes(); |
| } |
| |
| 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 { |
| mWindowManagerService.statusBarVisibilityChanged(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 (mNetworkController != null) { |
| mNetworkController.dump(fd, pw, args); |
| } |
| if (mBluetoothController != null) { |
| mBluetoothController.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 (mSecurityController != null) { |
| mSecurityController.dump(fd, pw, args); |
| } |
| if (mHeadsUpManager != null) { |
| mHeadsUpManager.dump(fd, pw, args); |
| } else { |
| pw.println(" mHeadsUpManager: null"); |
| } |
| |
| 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, |
| final boolean dismissShade) { |
| if (onlyProvisioned && !isDeviceProvisioned()) return; |
| |
| final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity( |
| mContext, intent, mCurrentUserId); |
| 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(); |
| } |
| intent.setFlags( |
| Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); |
| mContext.startActivityAsUser( |
| intent, new UserHandle(UserHandle.USER_CURRENT)); |
| overrideActivityPendingAppTransition( |
| keyguardShowing && !afterKeyguardGone); |
| } catch (RemoteException e) { |
| } |
| } |
| }); |
| if (dismissShade) { |
| animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */, |
| true /* delayed*/); |
| } |
| return true; |
| } |
| }, 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); |
| } |
| else 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 ("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(final OnDismissAction action, |
| boolean afterKeyguardGone) { |
| if (mStatusBarKeyguardViewManager.isShowing()) { |
| mStatusBarKeyguardViewManager.dismissWithAction(action, 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(); |
| updateShowSearchHoldoff(); |
| updateRowStates(); |
| mIconController.updateResources(); |
| mScreenPinningRequest.onConfigurationChanged(); |
| } |
| |
| @Override |
| public void userSwitched(int newUserId) { |
| super.userSwitched(newUserId); |
| if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId); |
| animateCollapsePanels(); |
| updatePublicMode(); |
| updateNotifications(); |
| resetUserSetupObserver(); |
| setControllerUsers(); |
| } |
| |
| private void setControllerUsers() { |
| if (mZenModeController != null) { |
| mZenModeController.setUserId(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); |
| |
| mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); |
| |
| 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.<String>emptyList(), mCurrentlyVisibleNotifications); |
| mCurrentlyVisibleNotifications.clear(); |
| } |
| 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<String> newlyVisible, Collection<String> noLongerVisible) { |
| if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) { |
| return; |
| } |
| String[] newlyVisibleAr = newlyVisible.toArray(new String[newlyVisible.size()]); |
| String[] noLongerVisibleAr = noLongerVisible.toArray(new String[noLongerVisible.size()]); |
| try { |
| mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr); |
| } catch (RemoteException e) { |
| // Ignore. |
| } |
| } |
| |
| // State logging |
| |
| private void logStateToEventlog() { |
| boolean isShowing = mStatusBarKeyguardViewManager.isShowing(); |
| boolean isOccluded = mStatusBarKeyguardViewManager.isOccluded(); |
| boolean isBouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing(); |
| boolean isSecure = mUnlockMethodCache.isMethodSecure(); |
| boolean isCurrentlyInsecure = mUnlockMethodCache.isCurrentlyInsecure(); |
| int stateFingerprint = getLoggingFingerprint(mState, |
| isShowing, |
| isOccluded, |
| isBouncerShowing, |
| isSecure, |
| isCurrentlyInsecure); |
| if (stateFingerprint != mLastLoggedStateFingerprint) { |
| EventLogTags.writeSysuiStatusBarState(mState, |
| isShowing ? 1 : 0, |
| isOccluded ? 1 : 0, |
| isBouncerShowing ? 1 : 0, |
| isSecure ? 1 : 0, |
| isCurrentlyInsecure ? 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() |
| || mExpandedVisible |
| || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0; |
| } |
| |
| public void postStartSettingsActivity(final Intent intent, int delay) { |
| mHandler.postDelayed(new Runnable() { |
| @Override |
| public void run() { |
| handleStartSettingsActivity(intent, true /*onlyProvisioned*/); |
| } |
| }, delay); |
| } |
| |
| private void handleStartSettingsActivity(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); |
| mAssistGestureManager.destroy(); |
| } |
| |
| private boolean mDemoModeAllowed; |
| private boolean mDemoMode; |
| |
| @Override |
| public void dispatchDemoCommand(String command, Bundle args) { |
| if (!mDemoModeAllowed) { |
| mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(), |
| "sysui_demo_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; |
| } |
| |
| public void showKeyguard() { |
| if (mLaunchTransitionFadingAway) { |
| mNotificationPanel.animate().cancel(); |
| mNotificationPanel.setAlpha(1f); |
| runLaunchTransitionEndRunnable(); |
| mLaunchTransitionFadingAway = false; |
| } |
| 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; |
| } |
| } |
| |
| 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(); |
| } |
| mNotificationPanel.setAlpha(1); |
| mNotificationPanel.animate() |
| .alpha(0) |
| .setStartDelay(FADE_KEYGUARD_START_DELAY) |
| .setDuration(FADE_KEYGUARD_DURATION) |
| .withLayer() |
| .withEndAction(new Runnable() { |
| @Override |
| public void run() { |
| mNotificationPanel.setAlpha(1); |
| runLaunchTransitionEndRunnable(); |
| mLaunchTransitionFadingAway = false; |
| } |
| }); |
| 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(mDisabledUnmodified, true /* 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); |
| } 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() { |
| if (mState != StatusBarState.KEYGUARD && !mNotificationPanel.isDozing()) { |
| return; |
| } |
| 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); |
| } |
| |
| @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 onPhoneHintStarted() { |
| mKeyguardIndicationController.showTransientIndication(R.string.phone_hint); |
| } |
| |
| public void onTrackingStopped(boolean expand) { |
| if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { |
| if (!expand && !mUnlockMethodCache.isCurrentlyInsecure()) { |
| 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); |
| disable(mDisabledUnmodified, 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(mDisabled, 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(mDisabled, 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); |
| mScreenOnComingFromTouch = true; |
| mScreenOnTouchLocation = new PointF(event.getX(), event.getY()); |
| mNotificationPanel.setTouchDisabled(false); |
| } |
| } |
| |
| @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 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 (!mDozing) { |
| mDozing = true; |
| DozeLog.traceDozing(mContext, mDozing); |
| updateDozingState(); |
| } |
| ready.run(); |
| } |
| |
| private void handlePulseWhileDozing(@NonNull PulseCallback callback, int reason) { |
| mDozeScrimController.pulse(callback, reason); |
| } |
| |
| private void handleStopDozing() { |
| if (mDozing) { |
| mDozing = false; |
| DozeLog.traceDozing(mContext, mDozing); |
| updateDozingState(); |
| } |
| } |
| |
| 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; |
| } |
| } |
| } |
| } |
| } |