| /* |
| * 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 static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; |
| 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_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_LIGHTS_OUT; |
| |
| import android.animation.Animator; |
| import android.animation.AnimatorListenerAdapter; |
| import android.animation.ObjectAnimator; |
| import android.animation.TimeInterpolator; |
| import android.app.ActivityManager; |
| import android.app.ActivityManagerNative; |
| import android.app.Notification; |
| import android.app.PendingIntent; |
| import android.app.StatusBarManager; |
| import android.content.BroadcastReceiver; |
| 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.Canvas; |
| import android.graphics.ColorFilter; |
| import android.graphics.PixelFormat; |
| import android.graphics.Point; |
| import android.graphics.PorterDuff; |
| import android.graphics.Rect; |
| import android.graphics.drawable.Drawable; |
| import android.inputmethodservice.InputMethodService; |
| import android.media.AudioManager; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.Message; |
| import android.os.PowerManager; |
| import android.os.RemoteException; |
| import android.os.SystemClock; |
| import android.os.UserHandle; |
| import android.provider.Settings; |
| import android.service.notification.StatusBarNotification; |
| import android.util.DisplayMetrics; |
| import android.util.EventLog; |
| import android.util.Log; |
| import android.view.Display; |
| import android.view.Gravity; |
| import android.view.MotionEvent; |
| import android.view.VelocityTracker; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.ViewGroup.LayoutParams; |
| import android.view.ViewPropertyAnimator; |
| import android.view.ViewStub; |
| import android.view.WindowManager; |
| import android.view.animation.AccelerateInterpolator; |
| import android.view.animation.Animation; |
| import android.view.animation.AnimationUtils; |
| import android.view.animation.DecelerateInterpolator; |
| import android.widget.FrameLayout; |
| import android.widget.ImageView; |
| import android.widget.LinearLayout; |
| import android.widget.TextView; |
| |
| import com.android.internal.statusbar.StatusBarIcon; |
| import com.android.systemui.DemoMode; |
| import com.android.systemui.EventLogTags; |
| import com.android.systemui.R; |
| import com.android.systemui.SwipeHelper; |
| import com.android.systemui.statusbar.BaseStatusBar; |
| import com.android.systemui.statusbar.CommandQueue; |
| import com.android.systemui.statusbar.GestureRecorder; |
| import com.android.systemui.statusbar.NotificationData; |
| import com.android.systemui.statusbar.NotificationData.Entry; |
| import com.android.systemui.statusbar.SignalClusterView; |
| import com.android.systemui.statusbar.StatusBarIconView; |
| |
| import com.android.systemui.statusbar.policy.BatteryController; |
| import com.android.systemui.statusbar.policy.BluetoothController; |
| import com.android.systemui.statusbar.policy.DateView; |
| import com.android.systemui.statusbar.policy.HeadsUpNotificationView; |
| import com.android.systemui.statusbar.policy.LocationController; |
| import com.android.systemui.statusbar.policy.NetworkController; |
| import com.android.systemui.statusbar.policy.NotificationRowLayout; |
| import com.android.systemui.statusbar.policy.OnSizeChangedListener; |
| import com.android.systemui.statusbar.policy.RotationLockController; |
| |
| import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| |
| public class PhoneStatusBar extends BaseStatusBar implements DemoMode { |
| 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_WINDOW_STATE = false; |
| |
| public static final boolean SETTINGS_DRAG_SHORTCUT = true; |
| |
| // additional instrumentation for testing purposes; intended to be left on during development |
| public static final boolean CHATTY = DEBUG; |
| |
| public static final String ACTION_STATUSBAR_START |
| = "com.android.internal.policy.statusbar.START"; |
| |
| 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; |
| // 1020-1030 reserved for BaseStatusBar |
| |
| private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true; |
| |
| private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService |
| private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER; |
| |
| private static final int STATUS_OR_NAV_TRANSIENT = |
| View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT; |
| private static final long AUTOHIDE_TIMEOUT_MS = 3000; |
| |
| // fling gesture tuning parameters, scaled to display density |
| private float mSelfExpandVelocityPx; // classic value: 2000px/s |
| private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up") |
| private float mFlingExpandMinVelocityPx; // classic value: 200px/s |
| private float mFlingCollapseMinVelocityPx; // classic value: 200px/s |
| private float mCollapseMinDisplayFraction; // classic value: 0.08 (25px/min(320px,480px) on G1) |
| private float mExpandMinDisplayFraction; // classic value: 0.5 (drag open halfway to expand) |
| private float mFlingGestureMaxXVelocityPx; // classic value: 150px/s |
| |
| private float mExpandAccelPx; // classic value: 2000px/s/s |
| private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up") |
| |
| private float mFlingGestureMaxOutputVelocityPx; // how fast can it really go? (should be a little |
| // faster than mSelfCollapseVelocityPx) |
| |
| PhoneStatusBarPolicy mIconPolicy; |
| |
| // These are no longer handled by the policy, because we need custom strategies for them |
| BluetoothController mBluetoothController; |
| BatteryController mBatteryController; |
| LocationController mLocationController; |
| NetworkController mNetworkController; |
| RotationLockController mRotationLockController; |
| |
| int mNaturalBarHeight = -1; |
| int mIconSize = -1; |
| int mIconHPadding = -1; |
| Display mDisplay; |
| Point mCurrentDisplaySize = new Point(); |
| private float mHeadsUpVerticalOffset; |
| private int[] mPilePosition = new int[2]; |
| |
| StatusBarWindowView mStatusBarWindow; |
| PhoneStatusBarView mStatusBarView; |
| private int mStatusBarWindowState = WINDOW_STATE_SHOWING; |
| |
| int mPixelFormat; |
| Object mQueueLock = new Object(); |
| |
| // viewgroup containing the normal contents of the statusbar |
| LinearLayout mStatusBarContents; |
| |
| // right-hand icons |
| LinearLayout mSystemIconArea; |
| |
| // left-hand icons |
| LinearLayout mStatusIcons; |
| // the icons themselves |
| IconMerger mNotificationIcons; |
| // [+> |
| View mMoreIcon; |
| // mode indicator icon |
| ImageView mModeIcon; |
| |
| // expanded notifications |
| NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window |
| View mNotificationScroller; |
| View mExpandedContents; |
| int mNotificationPanelGravity; |
| int mNotificationPanelMarginBottomPx, mNotificationPanelMarginPx; |
| float mNotificationPanelMinHeightFrac; |
| boolean mNotificationPanelIsFullScreenWidth; |
| TextView mNotificationPanelDebugText; |
| |
| // settings |
| QuickSettings mQS; |
| boolean mHasSettingsPanel, mHasFlipSettings; |
| SettingsPanelView mSettingsPanel; |
| View mFlipSettingsView; |
| QuickSettingsContainerView mSettingsContainer; |
| int mSettingsPanelGravity; |
| |
| // top bar |
| View mNotificationPanelHeader; |
| View mDateTimeView; |
| View mClearButton; |
| ImageView mSettingsButton, mNotificationButton; |
| |
| // carrier/wifi label |
| private TextView mCarrierLabel; |
| private boolean mCarrierLabelVisible = false; |
| private int mCarrierLabelHeight; |
| private TextView mEmergencyCallLabel; |
| private int mNotificationHeaderHeight; |
| |
| private boolean mShowCarrierInPanel = false; |
| |
| // position |
| int[] mPositionTmp = new int[2]; |
| boolean mExpandedVisible; |
| |
| // the date view |
| DateView mDateView; |
| |
| // for heads up notifications |
| private HeadsUpNotificationView mHeadsUpNotificationView; |
| private int mHeadsUpNotificationDecay; |
| |
| // on-screen navigation buttons |
| private NavigationBarView mNavigationBarView = null; |
| private int mNavigationBarWindowState = WINDOW_STATE_SHOWING; |
| |
| // the tracker view |
| int mTrackingPosition; // the position of the top of the tracking view. |
| |
| // ticker |
| private Ticker mTicker; |
| private View mTickerView; |
| private boolean mTicking; |
| |
| // Tracking finger for opening/closing. |
| int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore |
| boolean mTracking; |
| VelocityTracker mVelocityTracker; |
| |
| int[] mAbsPos = new int[2]; |
| Runnable mPostCollapseCleanup = null; |
| |
| // 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 int mNavigationIconHints = 0; |
| private final Animator.AnimatorListener mMakeIconsInvisible = new AnimatorListenerAdapter() { |
| @Override |
| public void onAnimationEnd(Animator animation) { |
| // double-check to avoid races |
| if (mStatusBarContents.getAlpha() == 0) { |
| if (DEBUG) Log.d(TAG, "makeIconsInvisible"); |
| mStatusBarContents.setVisibility(View.INVISIBLE); |
| } |
| } |
| }; |
| |
| // 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 (mSettingsButton != null && mHasFlipSettings) { |
| mSettingsButton.setVisibility(userSetup ? View.VISIBLE : View.INVISIBLE); |
| } |
| if (mSettingsPanel != null) { |
| mSettingsPanel.setEnabled(userSetup); |
| } |
| 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 && 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"); |
| setHeadsUpVisibility(false); |
| mHeadsUpNotificationView.setNotification(null); |
| removeHeadsUpView(); |
| } else { |
| addHeadsUpView(); |
| } |
| } |
| } |
| }; |
| |
| private int mInteractingWindows; |
| private boolean mAutohideSuspended; |
| private int mStatusBarMode; |
| private int mNavigationBarMode; |
| private Boolean mScreenOn; |
| |
| private final Runnable mAutohide = new Runnable() { |
| @Override |
| public void run() { |
| int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT; |
| if (mSystemUiVisibility != requested) { |
| notifyUiVisibilityChanged(requested); |
| } |
| }}; |
| |
| @Override |
| public void setZenMode(int mode) { |
| super.setZenMode(mode); |
| if (mModeIcon == null) return; |
| if (!isDeviceProvisioned()) return; |
| mModeIcon.setVisibility(mode != Settings.Global.ZEN_MODE_OFF ? View.VISIBLE : View.GONE); |
| } |
| |
| @Override |
| public void start() { |
| mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)) |
| .getDefaultDisplay(); |
| updateDisplaySize(); |
| |
| super.start(); // calls createAndAddWindows() |
| |
| addNavigationBar(); |
| |
| // Lastly, call to the icon policy to install/update all the icons. |
| mIconPolicy = new PhoneStatusBarPolicy(mContext); |
| 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); |
| } |
| } |
| |
| // ================================================================================ |
| // Constructing the view |
| // ================================================================================ |
| protected PhoneStatusBarView makeStatusBarView() { |
| final Context context = mContext; |
| |
| Resources res = context.getResources(); |
| |
| updateDisplaySize(); // populates mDisplayMetrics |
| loadDimens(); |
| |
| mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size); |
| |
| 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); |
| mNotificationPanelIsFullScreenWidth = |
| (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT); |
| |
| // make the header non-responsive to clicks |
| mNotificationPanel.findViewById(R.id.header).setOnTouchListener( |
| new View.OnTouchListener() { |
| @Override |
| public boolean onTouch(View v, MotionEvent event) { |
| return true; // e eats everything |
| } |
| }); |
| |
| if (!ActivityManager.isHighEndGfx()) { |
| mStatusBarWindow.setBackground(null); |
| mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor( |
| R.color.notification_panel_solid_background))); |
| } |
| if (ENABLE_HEADS_UP) { |
| mHeadsUpNotificationView = |
| (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null); |
| mHeadsUpNotificationView.setVisibility(View.GONE); |
| mHeadsUpNotificationView.setBar(this); |
| } |
| 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.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; |
| |
| mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area); |
| mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons); |
| mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons); |
| mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon); |
| mNotificationIcons.setOverflowIndicator(mMoreIcon); |
| mModeIcon = (ImageView)mStatusBarView.findViewById(R.id.modeIcon); |
| mModeIcon.setImageResource(R.drawable.stat_sys_zen_limited); |
| mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents); |
| mTickerView = mStatusBarView.findViewById(R.id.ticker); |
| |
| View legacyScrollView = mStatusBarWindow.findViewById(R.id.scroll); |
| NotificationStackScrollLayout notificationStack |
| = (NotificationStackScrollLayout) mStatusBarWindow |
| .findViewById(R.id.notification_stack_scroller); |
| if (ENABLE_NOTIFICATION_STACK) { |
| notificationStack.setLongPressListener(getNotificationLongClicker()); |
| mPile = notificationStack; |
| legacyScrollView.setVisibility(View.GONE); |
| |
| // The scrollview and the notification container are unified now! |
| // TODO: remove mNotificationScroller entirely once we fully switch to the new Layout |
| mNotificationScroller = notificationStack; |
| } else { |
| mNotificationScroller = legacyScrollView; |
| // less drawing during pulldowns |
| mNotificationScroller.setVerticalScrollBarEnabled(false); |
| NotificationRowLayout rowLayout |
| = (NotificationRowLayout) mStatusBarWindow.findViewById(R.id.latestItems); |
| rowLayout.setLayoutTransitionsEnabled(false); |
| rowLayout.setLongPressListener(getNotificationLongClicker()); |
| mPile = rowLayout; |
| notificationStack.setVisibility(View.GONE); |
| } |
| |
| mExpandedContents = mPile; // was: expanded.findViewById(R.id.notificationLinearLayout); |
| |
| |
| |
| mNotificationPanelHeader = mStatusBarWindow.findViewById(R.id.header); |
| |
| mClearButton = mStatusBarWindow.findViewById(R.id.clear_all_button); |
| mClearButton.setOnClickListener(mClearButtonListener); |
| mClearButton.setAlpha(0f); |
| mClearButton.setVisibility(View.INVISIBLE); |
| mClearButton.setEnabled(false); |
| mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date); |
| |
| mHasSettingsPanel = res.getBoolean(R.bool.config_hasSettingsPanel); |
| mHasFlipSettings = res.getBoolean(R.bool.config_hasFlipSettingsPanel); |
| |
| mDateTimeView = mNotificationPanelHeader.findViewById(R.id.datetime); |
| if (mDateTimeView != null) { |
| mDateTimeView.setOnClickListener(mClockClickListener); |
| mDateTimeView.setEnabled(true); |
| } |
| |
| mSettingsButton = (ImageView) mStatusBarWindow.findViewById(R.id.settings_button); |
| if (mSettingsButton != null) { |
| mSettingsButton.setOnClickListener(mSettingsButtonListener); |
| if (mHasSettingsPanel) { |
| if (mStatusBarView.hasFullWidthNotifications()) { |
| // the settings panel is hiding behind this button |
| mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings); |
| mSettingsButton.setVisibility(View.VISIBLE); |
| } else { |
| // there is a settings panel, but it's on the other side of the (large) screen |
| final View buttonHolder = mStatusBarWindow.findViewById( |
| R.id.settings_button_holder); |
| if (buttonHolder != null) { |
| buttonHolder.setVisibility(View.GONE); |
| } |
| } |
| } else { |
| // no settings panel, go straight to settings |
| mSettingsButton.setVisibility(View.VISIBLE); |
| mSettingsButton.setImageResource(R.drawable.ic_notify_settings); |
| } |
| } |
| if (mHasFlipSettings) { |
| mNotificationButton = (ImageView) mStatusBarWindow.findViewById(R.id.notification_button); |
| if (mNotificationButton != null) { |
| mNotificationButton.setOnClickListener(mNotificationButtonListener); |
| } |
| } |
| |
| if (!mNotificationPanelIsFullScreenWidth) { |
| mNotificationPanel.setSystemUiVisibility( |
| View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS | |
| View.STATUS_BAR_DISABLE_CLOCK); |
| } |
| |
| mTicker = new MyTicker(context, mStatusBarView); |
| |
| TickerView tickerView = (TickerView)mStatusBarView.findViewById(R.id.tickerText); |
| tickerView.mTicker = mTicker; |
| |
| mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); |
| |
| // set the inital view visibility |
| setAreThereNotifications(); |
| |
| // Other icons |
| mLocationController = new LocationController(mContext); // will post a notification |
| mBatteryController = new BatteryController(mContext); |
| mNetworkController = new NetworkController(mContext); |
| mBluetoothController = new BluetoothController(mContext); |
| if (mContext.getResources().getBoolean(R.bool.config_showRotationLock) |
| || QuickSettings.DEBUG_GONE_TILES) { |
| mRotationLockController = new RotationLockController(mContext); |
| } |
| final SignalClusterView signalCluster = |
| (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster); |
| |
| |
| mNetworkController.addSignalCluster(signalCluster); |
| signalCluster.setNetworkController(mNetworkController); |
| |
| final boolean isAPhone = mNetworkController.hasVoiceCallingFeature(); |
| if (isAPhone) { |
| mEmergencyCallLabel = |
| (TextView) mStatusBarWindow.findViewById(R.id.emergency_calls_only); |
| if (mEmergencyCallLabel != null) { |
| mNetworkController.addEmergencyLabelView(mEmergencyCallLabel); |
| mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() { |
| public void onClick(View v) { }}); |
| mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { |
| @Override |
| public void onLayoutChange(View v, int left, int top, int right, int bottom, |
| int oldLeft, int oldTop, int oldRight, int oldBottom) { |
| updateCarrierLabelVisibility(false); |
| }}); |
| } |
| } |
| |
| mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label); |
| mShowCarrierInPanel = (mCarrierLabel != null); |
| if (DEBUG) Log.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel); |
| if (mShowCarrierInPanel) { |
| mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE); |
| |
| // for mobile devices, we always show mobile connection info here (SPN/PLMN) |
| // for other devices, we show whatever network is connected |
| if (mNetworkController.hasMobileDataFeature()) { |
| mNetworkController.addMobileLabelView(mCarrierLabel); |
| } else { |
| mNetworkController.addCombinedLabelView(mCarrierLabel); |
| } |
| |
| // set up the dynamic hide/show of the label |
| if (!ENABLE_NOTIFICATION_STACK) |
| ((NotificationRowLayout) mPile).setOnSizeChangedListener(new OnSizeChangedListener() { |
| @Override |
| public void onSizeChanged(View view, int w, int h, int oldw, int oldh) { |
| updateCarrierLabelVisibility(false); |
| } |
| }); |
| } |
| |
| // Quick Settings (where available, some restrictions apply) |
| if (mHasSettingsPanel) { |
| // first, figure out where quick settings should be inflated |
| final View settings_stub; |
| if (mHasFlipSettings) { |
| // a version of quick settings that flips around behind the notifications |
| settings_stub = mStatusBarWindow.findViewById(R.id.flip_settings_stub); |
| if (settings_stub != null) { |
| mFlipSettingsView = ((ViewStub)settings_stub).inflate(); |
| mFlipSettingsView.setVisibility(View.GONE); |
| mFlipSettingsView.setVerticalScrollBarEnabled(false); |
| } |
| } else { |
| // full quick settings panel |
| settings_stub = mStatusBarWindow.findViewById(R.id.quick_settings_stub); |
| if (settings_stub != null) { |
| mSettingsPanel = (SettingsPanelView) ((ViewStub)settings_stub).inflate(); |
| } else { |
| mSettingsPanel = (SettingsPanelView) mStatusBarWindow.findViewById(R.id.settings_panel); |
| } |
| |
| if (mSettingsPanel != null) { |
| if (!ActivityManager.isHighEndGfx()) { |
| mSettingsPanel.setBackground(new FastColorDrawable(context.getResources().getColor( |
| R.color.notification_panel_solid_background))); |
| } |
| } |
| } |
| |
| // wherever you find it, Quick Settings needs a container to survive |
| mSettingsContainer = (QuickSettingsContainerView) |
| mStatusBarWindow.findViewById(R.id.quick_settings_container); |
| if (mSettingsContainer != null) { |
| mQS = new QuickSettings(mContext, mSettingsContainer); |
| if (!mNotificationPanelIsFullScreenWidth) { |
| mSettingsContainer.setSystemUiVisibility( |
| View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS |
| | View.STATUS_BAR_DISABLE_SYSTEM_INFO); |
| } |
| if (mSettingsPanel != null) { |
| mSettingsPanel.setQuickSettings(mQS); |
| } |
| mQS.setService(this); |
| mQS.setBar(mStatusBarView); |
| mQS.setup(mNetworkController, mBluetoothController, mBatteryController, |
| mLocationController, mRotationLockController); |
| } else { |
| mQS = null; // fly away, be free |
| } |
| } |
| |
| PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); |
| mBroadcastReceiver.onReceive(mContext, |
| new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF)); |
| |
| // receive broadcasts |
| IntentFilter filter = new IntentFilter(); |
| filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); |
| filter.addAction(Intent.ACTION_SCREEN_OFF); |
| filter.addAction(Intent.ACTION_SCREEN_ON); |
| filter.addAction(ACTION_DEMO); |
| context.registerReceiver(mBroadcastReceiver, filter); |
| |
| // listen for USER_SETUP_COMPLETE setting (per-user) |
| resetUserSetupObserver(); |
| |
| return mStatusBarView; |
| } |
| |
| @Override |
| protected void onShowSearchPanel() { |
| if (mNavigationBarView != null) { |
| mNavigationBarView.getBarTransitions().setContentVisible(false); |
| } |
| } |
| |
| @Override |
| protected void onHideSearchPanel() { |
| if (mNavigationBarView != null) { |
| mNavigationBarView.getBarTransitions().setContentVisible(true); |
| } |
| } |
| |
| @Override |
| protected View getStatusBarView() { |
| return mStatusBarView; |
| } |
| |
| @Override |
| protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) { |
| boolean opaque = false; |
| WindowManager.LayoutParams lp = new WindowManager.LayoutParams( |
| LayoutParams.MATCH_PARENT, |
| LayoutParams.MATCH_PARENT, |
| WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, |
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
| | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
| | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, |
| (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT)); |
| if (ActivityManager.isHighEndGfx()) { |
| lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; |
| } |
| lp.gravity = Gravity.BOTTOM | Gravity.START; |
| lp.setTitle("SearchPanel"); |
| // TODO: Define custom animation for Search panel |
| lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications; |
| lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED |
| | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; |
| return lp; |
| } |
| |
| @Override |
| protected void updateSearchPanel() { |
| super.updateSearchPanel(); |
| if (mNavigationBarView != null) { |
| mNavigationBarView.setDelegateView(mSearchPanelView); |
| } |
| } |
| |
| @Override |
| public void showSearchPanel() { |
| super.showSearchPanel(); |
| mHandler.removeCallbacks(mShowSearchPanel); |
| |
| // we want to freeze the sysui state wherever it is |
| mSearchPanelView.setSystemUiVisibility(mSystemUiVisibility); |
| |
| if (mNavigationBarView != null) { |
| WindowManager.LayoutParams lp = |
| (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams(); |
| lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; |
| mWindowManager.updateViewLayout(mNavigationBarView, lp); |
| } |
| } |
| |
| @Override |
| public void hideSearchPanel() { |
| super.hideSearchPanel(); |
| if (mNavigationBarView != null) { |
| WindowManager.LayoutParams lp = |
| (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams(); |
| lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; |
| mWindowManager.updateViewLayout(mNavigationBarView, lp); |
| } |
| } |
| |
| protected int getStatusBarGravity() { |
| return Gravity.TOP | Gravity.FILL_HORIZONTAL; |
| } |
| |
| 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 int mShowSearchHoldoff = 0; |
| private Runnable mShowSearchPanel = new Runnable() { |
| public void run() { |
| showSearchPanel(); |
| awakenDreams(); |
| } |
| }; |
| |
| View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() { |
| public boolean onTouch(View v, MotionEvent event) { |
| switch(event.getAction()) { |
| case MotionEvent.ACTION_DOWN: |
| if (!shouldDisableNavbarGestures()) { |
| mHandler.removeCallbacks(mShowSearchPanel); |
| mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff); |
| } |
| break; |
| |
| case MotionEvent.ACTION_UP: |
| case MotionEvent.ACTION_CANCEL: |
| mHandler.removeCallbacks(mShowSearchPanel); |
| 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.getHomeButton().setOnTouchListener(mHomeSearchActionListener); |
| mNavigationBarView.getSearchLight().setOnTouchListener(mHomeSearchActionListener); |
| updateSearchPanel(); |
| } |
| |
| // 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; |
| } |
| |
| private void addHeadsUpView() { |
| WindowManager.LayoutParams lp = new WindowManager.LayoutParams( |
| LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, |
| WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar! |
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
| | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
| | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
| | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
| | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM |
| | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, |
| PixelFormat.TRANSLUCENT); |
| lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; |
| lp.gravity = Gravity.TOP; |
| lp.setTitle("Heads Up"); |
| lp.packageName = mContext.getPackageName(); |
| lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp; |
| |
| mWindowManager.addView(mHeadsUpNotificationView, lp); |
| } |
| |
| private void removeHeadsUpView() { |
| mWindowManager.removeView(mHeadsUpNotificationView); |
| } |
| |
| public void refreshAllStatusBarIcons() { |
| refreshAllIconsForLayout(mStatusIcons); |
| refreshAllIconsForLayout(mNotificationIcons); |
| } |
| |
| private void refreshAllIconsForLayout(LinearLayout ll) { |
| final int count = ll.getChildCount(); |
| for (int n = 0; n < count; n++) { |
| View child = ll.getChildAt(n); |
| if (child instanceof StatusBarIconView) { |
| ((StatusBarIconView) child).updateDrawable(); |
| } |
| } |
| } |
| |
| public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) { |
| if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex |
| + " icon=" + icon); |
| StatusBarIconView view = new StatusBarIconView(mContext, slot, null); |
| view.set(icon); |
| mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize)); |
| } |
| |
| public void updateIcon(String slot, int index, int viewIndex, |
| StatusBarIcon old, StatusBarIcon icon) { |
| if (SPEW) Log.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex |
| + " old=" + old + " icon=" + icon); |
| StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex); |
| view.set(icon); |
| } |
| |
| public void removeIcon(String slot, int index, int viewIndex) { |
| if (SPEW) Log.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex); |
| mStatusIcons.removeViewAt(viewIndex); |
| } |
| |
| public void addNotification(IBinder key, StatusBarNotification notification) { |
| if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore()); |
| Entry shadeEntry = createNotificationViews(key, notification); |
| if (shadeEntry == null) { |
| return; |
| } |
| if (!shouldIntercept(notification.getNotification())) { |
| if (mUseHeadsUp && shouldInterrupt(notification)) { |
| if (DEBUG) Log.d(TAG, "launching notification in heads up mode"); |
| Entry interruptionCandidate = new Entry(key, notification, null); |
| ViewGroup holder = mHeadsUpNotificationView.getHolder(); |
| if (inflateViewsForHeadsUp(interruptionCandidate, holder)) { |
| mInterruptingNotificationTime = System.currentTimeMillis(); |
| mInterruptingNotificationEntry = interruptionCandidate; |
| shadeEntry.setInterruption(); |
| |
| // 1. Populate mHeadsUpNotificationView |
| mHeadsUpNotificationView.setNotification(mInterruptingNotificationEntry); |
| |
| // 2. Animate mHeadsUpNotificationView in |
| mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP); |
| |
| // 3. Set alarm to age the notification off |
| resetHeadsUpDecayTimer(); |
| } |
| } else if (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 { |
| notification.getNotification().fullScreenIntent.send(); |
| } catch (PendingIntent.CanceledException e) { |
| } |
| } else { |
| // usual case: status bar visible & not immersive |
| |
| // show the ticker if there isn't already a heads up |
| if (mInterruptingNotificationEntry == null) { |
| tick(null, notification, true); |
| } |
| } |
| } |
| addNotificationViews(shadeEntry); |
| // Recalculate the position of the sliding windows and the titles. |
| setAreThereNotifications(); |
| updateExpandedViewPos(EXPANDED_LEAVE_ALONE); |
| } |
| |
| @Override |
| public void resetHeadsUpDecayTimer() { |
| mHandler.removeMessages(MSG_HIDE_HEADS_UP); |
| if (mUseHeadsUp && mHeadsUpNotificationDecay > 0 |
| && mHeadsUpNotificationView.isClearable()) { |
| mHandler.sendEmptyMessageDelayed(MSG_HIDE_HEADS_UP, mHeadsUpNotificationDecay); |
| } |
| } |
| |
| public void removeNotification(IBinder key) { |
| StatusBarNotification old = removeNotificationViews(key); |
| if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old); |
| |
| if (old != null) { |
| // Cancel the ticker if it's still running |
| mTicker.removeEntry(old); |
| |
| // Recalculate the position of the sliding windows and the titles. |
| updateExpandedViewPos(EXPANDED_LEAVE_ALONE); |
| |
| if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null |
| && old == mInterruptingNotificationEntry.notification) { |
| mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); |
| } |
| |
| if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0 |
| && !mNotificationPanel.isTracking()) { |
| animateCollapsePanels(); |
| } |
| } |
| |
| setAreThereNotifications(); |
| } |
| |
| @Override |
| protected void refreshLayout(int layoutDirection) { |
| if (mNavigationBarView != null) { |
| mNavigationBarView.setLayoutDirection(layoutDirection); |
| } |
| |
| if (mClearButton != null && mClearButton instanceof ImageView) { |
| // Force asset reloading |
| ((ImageView)mClearButton).setImageDrawable(null); |
| ((ImageView)mClearButton).setImageResource(R.drawable.ic_notify_clear); |
| } |
| |
| if (mSettingsButton != null) { |
| // Force asset reloading |
| mSettingsButton.setImageDrawable(null); |
| mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings); |
| } |
| |
| if (mNotificationButton != null) { |
| // Force asset reloading |
| mNotificationButton.setImageDrawable(null); |
| mNotificationButton.setImageResource(R.drawable.ic_notifications); |
| } |
| |
| refreshAllStatusBarIcons(); |
| } |
| |
| private void updateShowSearchHoldoff() { |
| mShowSearchHoldoff = mContext.getResources().getInteger( |
| R.integer.config_show_search_delay); |
| } |
| |
| private void loadNotificationShade() { |
| if (mPile == null) return; |
| |
| int N = mNotificationData.size(); |
| |
| ArrayList<View> toShow = new ArrayList<View>(); |
| |
| final boolean provisioned = isDeviceProvisioned(); |
| // If the device hasn't been through Setup, we only show system notifications |
| for (int i=0; i<N; i++) { |
| Entry ent = mNotificationData.get(N-i-1); |
| if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue; |
| |
| // TODO How do we want to badge notifcations from profiles. |
| if (!notificationIsForCurrentProfiles(ent.notification)) continue; |
| |
| final int vis = ent.notification.getNotification().visibility; |
| if (vis != Notification.VISIBILITY_SECRET) { |
| // when isLockscreenPublicMode() we show the public form of VISIBILITY_PRIVATE notifications |
| ent.row.setShowingPublic(isLockscreenPublicMode() |
| && vis == Notification.VISIBILITY_PRIVATE |
| && !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId())); |
| toShow.add(ent.row); |
| } |
| } |
| |
| ArrayList<View> toRemove = new ArrayList<View>(); |
| for (int i=0; i<mPile.getChildCount(); i++) { |
| View child = mPile.getChildAt(i); |
| if (!toShow.contains(child)) { |
| toRemove.add(child); |
| } |
| } |
| |
| for (View remove : toRemove) { |
| mPile.removeView(remove); |
| } |
| |
| for (int i=0; i<toShow.size(); i++) { |
| View v = toShow.get(i); |
| if (v.getParent() == null) { |
| mPile.addView(v, i); |
| } |
| } |
| |
| if (mSettingsButton != null) { |
| mSettingsButton.setEnabled(isDeviceProvisioned()); |
| } |
| } |
| |
| @Override |
| protected void updateNotificationIcons() { |
| if (mNotificationIcons == null) return; |
| |
| loadNotificationShade(); |
| |
| final LinearLayout.LayoutParams params |
| = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight); |
| |
| int N = mNotificationData.size(); |
| |
| if (DEBUG) { |
| Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons); |
| } |
| |
| ArrayList<View> toShow = new ArrayList<View>(); |
| |
| final boolean provisioned = isDeviceProvisioned(); |
| // If the device hasn't been through Setup, we only show system notifications |
| for (int i=0; i<N; i++) { |
| Entry ent = mNotificationData.get(N-i-1); |
| if (!((provisioned && ent.notification.getScore() >= HIDE_ICONS_BELOW_SCORE) |
| || showNotificationEvenIfUnprovisioned(ent.notification))) continue; |
| if (!notificationIsForCurrentProfiles(ent.notification)) continue; |
| if (isLockscreenPublicMode() |
| && ent.notification.getNotification().visibility |
| == Notification.VISIBILITY_SECRET |
| && !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId())) { |
| // in "public" mode (atop a secure keyguard), secret notifs are totally hidden |
| continue; |
| } |
| if (shouldIntercept(ent.notification.getNotification())) { |
| continue; |
| } |
| toShow.add(ent.icon); |
| } |
| |
| ArrayList<View> toRemove = new ArrayList<View>(); |
| for (int i=0; i<mNotificationIcons.getChildCount(); i++) { |
| View child = mNotificationIcons.getChildAt(i); |
| if (!toShow.contains(child)) { |
| toRemove.add(child); |
| } |
| } |
| |
| for (View remove : toRemove) { |
| mNotificationIcons.removeView(remove); |
| } |
| |
| for (int i=0; i<toShow.size(); i++) { |
| View v = toShow.get(i); |
| if (v.getParent() == null) { |
| mNotificationIcons.addView(v, i, params); |
| } |
| } |
| } |
| |
| protected void updateCarrierLabelVisibility(boolean force) { |
| // TODO: Handle this for the notification stack scroller as well |
| if (!mShowCarrierInPanel) return; |
| // The idea here is to only show the carrier label when there is enough room to see it, |
| // i.e. when there aren't enough notifications to fill the panel. |
| if (SPEW) { |
| Log.d(TAG, String.format("pileh=%d scrollh=%d carrierh=%d", |
| mPile.getHeight(), mNotificationScroller.getHeight(), mCarrierLabelHeight)); |
| } |
| |
| final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null; |
| final boolean makeVisible = |
| !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly()) |
| && mPile.getHeight() < (mNotificationPanel.getHeight() - mCarrierLabelHeight - mNotificationHeaderHeight) |
| && mNotificationScroller.getVisibility() == View.VISIBLE; |
| |
| if (force || mCarrierLabelVisible != makeVisible) { |
| mCarrierLabelVisible = makeVisible; |
| if (DEBUG) { |
| Log.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible")); |
| } |
| mCarrierLabel.animate().cancel(); |
| if (makeVisible) { |
| mCarrierLabel.setVisibility(View.VISIBLE); |
| } |
| mCarrierLabel.animate() |
| .alpha(makeVisible ? 1f : 0f) |
| //.setStartDelay(makeVisible ? 500 : 0) |
| //.setDuration(makeVisible ? 750 : 100) |
| .setDuration(150) |
| .setListener(makeVisible ? null : new AnimatorListenerAdapter() { |
| @Override |
| public void onAnimationEnd(Animator animation) { |
| if (!mCarrierLabelVisible) { // race |
| mCarrierLabel.setVisibility(View.INVISIBLE); |
| mCarrierLabel.setAlpha(0f); |
| } |
| } |
| }) |
| .start(); |
| } |
| } |
| |
| @Override |
| protected void setAreThereNotifications() { |
| final boolean any = mNotificationData.size() > 0; |
| |
| final boolean clearable = any && mNotificationData.hasClearableItems(); |
| |
| if (SPEW) { |
| Log.d(TAG, "setAreThereNotifications: N=" + mNotificationData.size() |
| + " any=" + any + " clearable=" + clearable); |
| } |
| |
| if (mHasFlipSettings |
| && mFlipSettingsView != null |
| && mFlipSettingsView.getVisibility() == View.VISIBLE |
| && mNotificationScroller.getVisibility() != View.VISIBLE) { |
| // the flip settings panel is unequivocally showing; we should not be shown |
| mClearButton.setVisibility(View.INVISIBLE); |
| } else if (mClearButton.isShown()) { |
| if (clearable != (mClearButton.getAlpha() == 1.0f)) { |
| ObjectAnimator clearAnimation = ObjectAnimator.ofFloat( |
| mClearButton, "alpha", clearable ? 1.0f : 0.0f).setDuration(250); |
| clearAnimation.addListener(new AnimatorListenerAdapter() { |
| @Override |
| public void onAnimationEnd(Animator animation) { |
| if (mClearButton.getAlpha() <= 0.0f) { |
| mClearButton.setVisibility(View.INVISIBLE); |
| } |
| } |
| |
| @Override |
| public void onAnimationStart(Animator animation) { |
| if (mClearButton.getAlpha() <= 0.0f) { |
| mClearButton.setVisibility(View.VISIBLE); |
| } |
| } |
| }); |
| clearAnimation.start(); |
| } |
| } else { |
| mClearButton.setAlpha(clearable ? 1.0f : 0.0f); |
| mClearButton.setVisibility(clearable ? View.VISIBLE : View.INVISIBLE); |
| } |
| mClearButton.setEnabled(clearable); |
| |
| final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out); |
| final boolean showDot = (any&&!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(); |
| } |
| |
| updateCarrierLabelVisibility(false); |
| } |
| |
| public void showClock(boolean show) { |
| if (mStatusBarView == null) return; |
| View clock = mStatusBarView.findViewById(R.id.clock); |
| if (clock != null) { |
| clock.setVisibility(show ? View.VISIBLE : View.GONE); |
| } |
| } |
| |
| /** |
| * State is one or more of the DISABLE constants from StatusBarManager. |
| */ |
| public void disable(int 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_PRIVATE_NOTIFICATIONS) != 0) ? "PRIVATE" : "private"); |
| flagdbg.append(((diff & StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS) != 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) { |
| mSystemIconArea.animate().cancel(); |
| if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) { |
| mSystemIconArea.animate() |
| .alpha(0f) |
| .translationY(mNaturalBarHeight*0.5f) |
| .setDuration(175) |
| .setInterpolator(new DecelerateInterpolator(1.5f)) |
| .setListener(mMakeIconsInvisible) |
| .start(); |
| } else { |
| mSystemIconArea.setVisibility(View.VISIBLE); |
| mSystemIconArea.animate() |
| .alpha(1f) |
| .translationY(0) |
| .setStartDelay(0) |
| .setInterpolator(new DecelerateInterpolator(1.5f)) |
| .setDuration(175) |
| .start(); |
| } |
| } |
| |
| if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) { |
| boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0; |
| showClock(show); |
| } |
| 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_CLOSE_RECENTS_PANEL); |
| mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL); |
| } |
| } |
| |
| if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { |
| if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { |
| if (mTicking) { |
| haltTicker(); |
| } |
| |
| mNotificationIcons.animate() |
| .alpha(0f) |
| .translationY(mNaturalBarHeight*0.5f) |
| .setDuration(175) |
| .setInterpolator(new DecelerateInterpolator(1.5f)) |
| .setListener(mMakeIconsInvisible) |
| .start(); |
| } else { |
| mNotificationIcons.setVisibility(View.VISIBLE); |
| mNotificationIcons.animate() |
| .alpha(1f) |
| .translationY(0) |
| .setStartDelay(0) |
| .setInterpolator(new DecelerateInterpolator(1.5f)) |
| .setDuration(175) |
| .start(); |
| } |
| } else if ((diff & StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS) != 0) { |
| if ((state & StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS) != 0) { |
| // we are outside a secure keyguard, so we need to switch to "public" mode |
| setLockscreenPublicMode(true); |
| } else { |
| // user has authenticated the device; full notifications may be shown |
| setLockscreenPublicMode(false); |
| } |
| updateNotificationIcons(); |
| } |
| } |
| |
| @Override |
| protected BaseStatusBar.H createHandler() { |
| return new PhoneStatusBar.H(); |
| } |
| |
| /** |
| * 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_SHOW_HEADS_UP: |
| setHeadsUpVisibility(true); |
| break; |
| case MSG_HIDE_HEADS_UP: |
| setHeadsUpVisibility(false); |
| break; |
| case MSG_ESCALATE_HEADS_UP: |
| escalateHeadsUp(); |
| setHeadsUpVisibility(false); |
| break; |
| } |
| } |
| } |
| |
| /** if the interrupting notification had a fullscreen intent, fire it now. */ |
| private void escalateHeadsUp() { |
| if (mInterruptingNotificationEntry != null) { |
| final StatusBarNotification sbn = mInterruptingNotificationEntry.notification; |
| final Notification notification = sbn.getNotification(); |
| if (notification.fullScreenIntent != null) { |
| if (DEBUG) |
| Log.d(TAG, "converting a heads up to fullScreen"); |
| try { |
| notification.fullScreenIntent.send(); |
| } catch (PendingIntent.CanceledException e) { |
| } |
| } |
| } |
| } |
| |
| public Handler getHandler() { |
| return mHandler; |
| } |
| |
| View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() { |
| public void onFocusChange(View v, boolean hasFocus) { |
| // Because 'v' is a ViewGroup, all its children will be (un)selected |
| // too, which allows marqueeing to work. |
| v.setSelected(hasFocus); |
| } |
| }; |
| |
| boolean panelsEnabled() { |
| return (mDisabled & StatusBarManager.DISABLE_EXPAND) == 0; |
| } |
| |
| void makeExpandedVisible() { |
| if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); |
| if (mExpandedVisible || !panelsEnabled()) { |
| return; |
| } |
| |
| mExpandedVisible = true; |
| if (!ENABLE_NOTIFICATION_STACK) { |
| ((NotificationRowLayout) mPile).setLayoutTransitionsEnabled(true); |
| } |
| if (mNavigationBarView != null) |
| mNavigationBarView.setSlippery(true); |
| |
| updateCarrierLabelVisibility(true); |
| |
| updateExpandedViewPos(EXPANDED_LEAVE_ALONE); |
| |
| // 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! |
| WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams(); |
| lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; |
| lp.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; |
| lp.height = ViewGroup.LayoutParams.MATCH_PARENT; |
| mWindowManager.updateViewLayout(mStatusBarWindow, lp); |
| |
| visibilityChanged(true); |
| |
| setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); |
| } |
| |
| private void releaseFocus() { |
| if (mStatusBarWindow == null) return; |
| WindowManager.LayoutParams lp = |
| (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams(); |
| lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; |
| lp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; |
| mWindowManager.updateViewLayout(mStatusBarWindow, lp); |
| } |
| |
| public void animateCollapsePanels() { |
| animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); |
| } |
| |
| public void animateCollapsePanels(int flags) { |
| if (SPEW) { |
| Log.d(TAG, "animateCollapse():" |
| + " mExpandedVisible=" + mExpandedVisible |
| + " flags=" + flags); |
| } |
| |
| // release focus immediately to kick off focus change transition |
| releaseFocus(); |
| |
| if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) { |
| mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL); |
| mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL); |
| } |
| |
| if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) { |
| mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL); |
| mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL); |
| } |
| |
| if (mStatusBarWindow != null) { |
| mStatusBarWindow.cancelExpandHelper(); |
| mStatusBarView.collapseAllPanels(true); |
| } |
| } |
| |
| public ViewPropertyAnimator setVisibilityWhenDone( |
| final ViewPropertyAnimator a, final View v, final int vis) { |
| a.setListener(new AnimatorListenerAdapter() { |
| @Override |
| public void onAnimationEnd(Animator animation) { |
| v.setVisibility(vis); |
| a.setListener(null); // oneshot |
| } |
| }); |
| return a; |
| } |
| |
| public Animator setVisibilityWhenDone( |
| final Animator a, final View v, final int vis) { |
| a.addListener(new AnimatorListenerAdapter() { |
| @Override |
| public void onAnimationEnd(Animator animation) { |
| v.setVisibility(vis); |
| } |
| }); |
| return a; |
| } |
| |
| public Animator interpolator(TimeInterpolator ti, Animator a) { |
| a.setInterpolator(ti); |
| return a; |
| } |
| |
| public Animator startDelay(int d, Animator a) { |
| a.setStartDelay(d); |
| return a; |
| } |
| |
| public Animator start(Animator a) { |
| a.start(); |
| return a; |
| } |
| |
| final TimeInterpolator mAccelerateInterpolator = new AccelerateInterpolator(); |
| final TimeInterpolator mDecelerateInterpolator = new DecelerateInterpolator(); |
| final int FLIP_DURATION_OUT = 125; |
| final int FLIP_DURATION_IN = 225; |
| final int FLIP_DURATION = (FLIP_DURATION_IN + FLIP_DURATION_OUT); |
| |
| Animator mScrollViewAnim, mFlipSettingsViewAnim, mNotificationButtonAnim, |
| mSettingsButtonAnim, mClearButtonAnim; |
| |
| @Override |
| public void animateExpandNotificationsPanel() { |
| if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); |
| if (!panelsEnabled()) { |
| return ; |
| } |
| |
| mNotificationPanel.expand(); |
| if (mHasFlipSettings && mNotificationScroller.getVisibility() != View.VISIBLE) { |
| flipToNotifications(); |
| } |
| |
| if (false) postStartTracing(); |
| } |
| |
| public void flipToNotifications() { |
| if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel(); |
| if (mScrollViewAnim != null) mScrollViewAnim.cancel(); |
| if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel(); |
| if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel(); |
| if (mClearButtonAnim != null) mClearButtonAnim.cancel(); |
| |
| mNotificationScroller.setVisibility(View.VISIBLE); |
| mScrollViewAnim = start( |
| startDelay(FLIP_DURATION_OUT, |
| interpolator(mDecelerateInterpolator, |
| ObjectAnimator.ofFloat(mNotificationScroller, View.SCALE_X, 0f, 1f) |
| .setDuration(FLIP_DURATION_IN) |
| ))); |
| mFlipSettingsViewAnim = start( |
| setVisibilityWhenDone( |
| interpolator(mAccelerateInterpolator, |
| ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 1f, 0f) |
| ) |
| .setDuration(FLIP_DURATION_OUT), |
| mFlipSettingsView, View.INVISIBLE)); |
| mNotificationButtonAnim = start( |
| setVisibilityWhenDone( |
| ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 0f) |
| .setDuration(FLIP_DURATION), |
| mNotificationButton, View.INVISIBLE)); |
| mSettingsButton.setVisibility(View.VISIBLE); |
| mSettingsButtonAnim = start( |
| ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f) |
| .setDuration(FLIP_DURATION)); |
| mClearButton.setVisibility(View.VISIBLE); |
| mClearButton.setAlpha(0f); |
| setAreThereNotifications(); // this will show/hide the button as necessary |
| mNotificationPanel.postDelayed(new Runnable() { |
| public void run() { |
| updateCarrierLabelVisibility(false); |
| } |
| }, FLIP_DURATION - 150); |
| } |
| |
| @Override |
| public void animateExpandSettingsPanel() { |
| if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); |
| if (!panelsEnabled()) { |
| return; |
| } |
| |
| // Settings are not available in setup |
| if (!mUserSetup) return; |
| |
| if (mHasFlipSettings) { |
| mNotificationPanel.expand(); |
| if (mFlipSettingsView.getVisibility() != View.VISIBLE) { |
| flipToSettings(); |
| } |
| } else if (mSettingsPanel != null) { |
| mSettingsPanel.expand(); |
| } |
| |
| if (false) postStartTracing(); |
| } |
| |
| public void switchToSettings() { |
| // Settings are not available in setup |
| if (!mUserSetup) return; |
| |
| mFlipSettingsView.setScaleX(1f); |
| mFlipSettingsView.setVisibility(View.VISIBLE); |
| mSettingsButton.setVisibility(View.GONE); |
| mNotificationScroller.setVisibility(View.GONE); |
| mNotificationScroller.setScaleX(0f); |
| mNotificationButton.setVisibility(View.VISIBLE); |
| mNotificationButton.setAlpha(1f); |
| mClearButton.setVisibility(View.GONE); |
| } |
| |
| public void flipToSettings() { |
| // Settings are not available in setup |
| if (!mUserSetup) return; |
| |
| if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel(); |
| if (mScrollViewAnim != null) mScrollViewAnim.cancel(); |
| if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel(); |
| if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel(); |
| if (mClearButtonAnim != null) mClearButtonAnim.cancel(); |
| |
| mFlipSettingsView.setVisibility(View.VISIBLE); |
| mFlipSettingsView.setScaleX(0f); |
| mFlipSettingsViewAnim = start( |
| startDelay(FLIP_DURATION_OUT, |
| interpolator(mDecelerateInterpolator, |
| ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 0f, 1f) |
| .setDuration(FLIP_DURATION_IN) |
| ))); |
| mScrollViewAnim = start( |
| setVisibilityWhenDone( |
| interpolator(mAccelerateInterpolator, |
| ObjectAnimator.ofFloat(mNotificationScroller, View.SCALE_X, 1f, 0f) |
| ) |
| .setDuration(FLIP_DURATION_OUT), |
| mNotificationScroller, View.INVISIBLE)); |
| mSettingsButtonAnim = start( |
| setVisibilityWhenDone( |
| ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f) |
| .setDuration(FLIP_DURATION), |
| mNotificationScroller, View.INVISIBLE)); |
| mNotificationButton.setVisibility(View.VISIBLE); |
| mNotificationButtonAnim = start( |
| ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f) |
| .setDuration(FLIP_DURATION)); |
| mClearButtonAnim = start( |
| setVisibilityWhenDone( |
| ObjectAnimator.ofFloat(mClearButton, View.ALPHA, 0f) |
| .setDuration(FLIP_DURATION), |
| mClearButton, View.INVISIBLE)); |
| mNotificationPanel.postDelayed(new Runnable() { |
| public void run() { |
| updateCarrierLabelVisibility(false); |
| } |
| }, FLIP_DURATION - 150); |
| } |
| |
| public void flipPanels() { |
| if (mHasFlipSettings) { |
| if (mFlipSettingsView.getVisibility() != View.VISIBLE) { |
| flipToSettings(); |
| } else { |
| flipToNotifications(); |
| } |
| } |
| } |
| |
| public void animateCollapseQuickSettings() { |
| mStatusBarView.collapseAllPanels(true); |
| } |
| |
| void makeExpandedInvisibleSoon() { |
| mHandler.postDelayed(new Runnable() { public void run() { makeExpandedInvisible(); }}, 50); |
| } |
| |
| 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); |
| |
| if (mHasFlipSettings) { |
| // reset things to their proper state |
| if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel(); |
| if (mScrollViewAnim != null) mScrollViewAnim.cancel(); |
| if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel(); |
| if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel(); |
| if (mClearButtonAnim != null) mClearButtonAnim.cancel(); |
| |
| mNotificationScroller.setScaleX(1f); |
| mNotificationScroller.setVisibility(View.VISIBLE); |
| mSettingsButton.setAlpha(1f); |
| mSettingsButton.setVisibility(View.VISIBLE); |
| mNotificationPanel.setVisibility(View.GONE); |
| mFlipSettingsView.setVisibility(View.GONE); |
| mNotificationButton.setVisibility(View.GONE); |
| setAreThereNotifications(); // show the clear button |
| } |
| |
| mExpandedVisible = false; |
| if (!ENABLE_NOTIFICATION_STACK) { |
| ((NotificationRowLayout) mPile).setLayoutTransitionsEnabled(false); |
| } |
| if (mNavigationBarView != null) |
| mNavigationBarView.setSlippery(false); |
| visibilityChanged(false); |
| |
| // Shrink the window to the size of the status bar only |
| WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams(); |
| lp.height = getStatusBarHeight(); |
| lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; |
| lp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; |
| mWindowManager.updateViewLayout(mStatusBarWindow, lp); |
| |
| if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) { |
| setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); |
| } |
| |
| // Close any "App info" popups that might have snuck on-screen |
| dismissPopups(); |
| |
| if (mPostCollapseCleanup != null) { |
| mPostCollapseCleanup.run(); |
| mPostCollapseCleanup = null; |
| } |
| |
| setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); |
| } |
| |
| /** |
| * Enables or disables layers on the children of the notifications pile. |
| * |
| * When layers are enabled, this method attempts to enable layers for the minimal |
| * number of children. Only children visible when the notification area is fully |
| * expanded will receive a layer. The technique used in this method might cause |
| * more children than necessary to get a layer (at most one extra child with the |
| * current UI.) |
| * |
| * @param layerType {@link View#LAYER_TYPE_NONE} or {@link View#LAYER_TYPE_HARDWARE} |
| */ |
| private void setPileLayers(int layerType) { |
| final int count = mPile.getChildCount(); |
| |
| switch (layerType) { |
| case View.LAYER_TYPE_NONE: |
| for (int i = 0; i < count; i++) { |
| mPile.getChildAt(i).setLayerType(layerType, null); |
| } |
| break; |
| case View.LAYER_TYPE_HARDWARE: |
| final int[] location = new int[2]; |
| mNotificationPanel.getLocationInWindow(location); |
| |
| final int left = location[0]; |
| final int top = location[1]; |
| final int right = left + mNotificationPanel.getWidth(); |
| final int bottom = top + getExpandedViewMaxHeight(); |
| |
| final Rect childBounds = new Rect(); |
| |
| for (int i = 0; i < count; i++) { |
| final View view = mPile.getChildAt(i); |
| view.getLocationInWindow(location); |
| |
| childBounds.set(location[0], location[1], |
| location[0] + view.getWidth(), location[1] + view.getHeight()); |
| |
| if (childBounds.intersects(left, top, right, bottom)) { |
| view.setLayerType(layerType, null); |
| } |
| } |
| |
| break; |
| } |
| } |
| |
| 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) { |
| mStatusBarView.collapseAllPanels(false); |
| } |
| } |
| 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 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) { |
| 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(); |
| if (mTicking) { |
| haltTicker(); |
| } |
| } |
| |
| 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; |
| } |
| |
| // 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) { |
| return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT |
| : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT |
| : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT |
| : MODE_OPAQUE; |
| } |
| |
| private void checkBarModes() { |
| if (mDemoMode) return; |
| int sbMode = mStatusBarMode; |
| if (panelsEnabled() && (mInteractingWindows & StatusBarManager.WINDOW_STATUS_BAR) != 0) { |
| // if panels are expandable, force the status bar opaque on any interaction |
| sbMode = MODE_OPAQUE; |
| } |
| checkBarMode(sbMode, mStatusBarWindowState, mStatusBarView.getBarTransitions()); |
| if (mNavigationBarView != null) { |
| checkBarMode(mNavigationBarMode, |
| mNavigationBarWindowState, mNavigationBarView.getBarTransitions()); |
| } |
| } |
| |
| private void checkBarMode(int mode, int windowState, BarTransitions transitions) { |
| final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN; |
| 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) { |
| mInteractingWindows = interacting |
| ? (mInteractingWindows | barWindow) |
| : (mInteractingWindows & ~barWindow); |
| if (mInteractingWindows != 0) { |
| suspendAutohide(); |
| } else { |
| resumeSuspendedAutohide(); |
| } |
| 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 altBack = (backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) |
| || ((vis & InputMethodService.IME_VISIBLE) != 0); |
| |
| setNavigationIconHints( |
| altBack ? (mNavigationIconHints | NAVIGATION_HINT_BACK_ALT) |
| : (mNavigationIconHints & ~NAVIGATION_HINT_BACK_ALT)); |
| if (mQS != null) mQS.setImeWindowStatus(vis > 0); |
| } |
| |
| @Override |
| public void setHardKeyboardStatus(boolean available, boolean enabled) {} |
| |
| @Override |
| protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) { |
| // no ticking in lights-out mode |
| if (!areLightsOn()) return; |
| |
| // no ticking in Setup |
| if (!isDeviceProvisioned()) return; |
| |
| // not for you |
| if (!notificationIsForCurrentProfiles(n)) return; |
| |
| // Show the ticker if one is requested. Also don't do this |
| // until status bar window is attached to the window manager, |
| // because... well, what's the point otherwise? And trying to |
| // run a ticker without being attached will crash! |
| if (n.getNotification().tickerText != null && mStatusBarWindow != null |
| && mStatusBarWindow.getWindowToken() != null) { |
| if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS |
| | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) { |
| mTicker.addEntry(n); |
| } |
| } |
| } |
| |
| private class MyTicker extends Ticker { |
| MyTicker(Context context, View sb) { |
| super(context, sb); |
| } |
| |
| @Override |
| public void tickerStarting() { |
| mTicking = true; |
| mStatusBarContents.setVisibility(View.GONE); |
| mTickerView.setVisibility(View.VISIBLE); |
| mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null)); |
| mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null)); |
| } |
| |
| @Override |
| public void tickerDone() { |
| mStatusBarContents.setVisibility(View.VISIBLE); |
| mTickerView.setVisibility(View.GONE); |
| mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null)); |
| mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out, |
| mTickingDoneListener)); |
| } |
| |
| public void tickerHalting() { |
| if (mStatusBarContents.getVisibility() != View.VISIBLE) { |
| mStatusBarContents.setVisibility(View.VISIBLE); |
| mStatusBarContents |
| .startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null)); |
| } |
| mTickerView.setVisibility(View.GONE); |
| // we do not animate the ticker away at this point, just get rid of it (b/6992707) |
| } |
| } |
| |
| Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {; |
| public void onAnimationEnd(Animation animation) { |
| mTicking = false; |
| } |
| public void onAnimationRepeat(Animation animation) { |
| } |
| public void onAnimationStart(Animation animation) { |
| } |
| }; |
| |
| private Animation loadAnim(int id, Animation.AnimationListener listener) { |
| Animation anim = AnimationUtils.loadAnimation(mContext, id); |
| if (listener != null) { |
| anim.setAnimationListener(listener); |
| } |
| return anim; |
| } |
| |
| 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(" mTicking=" + mTicking); |
| pw.println(" mTracking=" + mTracking); |
| pw.println(" mDisplayMetrics=" + mDisplayMetrics); |
| pw.println(" mPile: " + viewInfo(mPile)); |
| pw.println(" mTickerView: " + viewInfo(mTickerView)); |
| pw.println(" mNotificationScroller: " + viewInfo(mNotificationScroller) |
| + " scroll " + mNotificationScroller.getScrollX() |
| + "," + mNotificationScroller.getScrollY()); |
| } |
| |
| pw.print(" mInteractingWindows="); pw.println(mInteractingWindows); |
| pw.print(" mStatusBarWindowState="); |
| pw.println(windowStateToString(mStatusBarWindowState)); |
| pw.print(" mStatusBarMode="); |
| pw.println(BarTransitions.modeToString(mStatusBarMode)); |
| pw.print(" mZenMode="); |
| pw.println(Settings.Global.zenModeToString(mZenMode)); |
| pw.print(" mUseHeadsUp="); |
| pw.println(mUseHeadsUp); |
| pw.print(" interrupting package: "); |
| pw.println(hunStateToString(mInterruptingNotificationEntry)); |
| 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.println(" Panels: "); |
| if (mNotificationPanel != null) { |
| pw.println(" mNotificationPanel=" + |
| mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug("")); |
| pw.print (" "); |
| mNotificationPanel.dump(fd, pw, args); |
| } |
| if (mSettingsPanel != null) { |
| pw.println(" mSettingsPanel=" + |
| mSettingsPanel + " params=" + mSettingsPanel.getLayoutParams().debug("")); |
| pw.print (" "); |
| mSettingsPanel.dump(fd, pw, args); |
| } |
| |
| if (DUMPTRUCK) { |
| synchronized (mNotificationData) { |
| int N = mNotificationData.size(); |
| pw.println(" notification icons: " + N); |
| for (int i=0; i<N; i++) { |
| NotificationData.Entry e = mNotificationData.get(i); |
| pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon); |
| StatusBarNotification n = e.notification; |
| pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " score=" + n.getScore()); |
| pw.println(" notification=" + n.getNotification()); |
| pw.println(" tickerText=\"" + n.getNotification().tickerText + "\""); |
| } |
| } |
| |
| int N = mStatusIcons.getChildCount(); |
| pw.println(" system icons: " + N); |
| for (int i=0; i<N; i++) { |
| StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i); |
| pw.println(" [" + i + "] icon=" + ic); |
| } |
| |
| 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); |
| } |
| |
| mNetworkController.dump(fd, pw, args); |
| } |
| |
| 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() { |
| // Put up the view |
| final int height = getStatusBarHeight(); |
| |
| // Now that the status bar window encompasses the sliding panel and its |
| // translucent backdrop, the entire thing is made TRANSLUCENT and is |
| // hardware-accelerated. |
| final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( |
| ViewGroup.LayoutParams.MATCH_PARENT, |
| height, |
| WindowManager.LayoutParams.TYPE_STATUS_BAR, |
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
| | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING |
| | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH |
| | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, |
| PixelFormat.TRANSLUCENT); |
| |
| lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; |
| |
| lp.gravity = getStatusBarGravity(); |
| lp.setTitle("StatusBar"); |
| lp.packageName = mContext.getPackageName(); |
| |
| makeStatusBarView(); |
| mWindowManager.addView(mStatusBarWindow, lp); |
| } |
| |
| void setNotificationIconVisibility(boolean visible, int anim) { |
| int old = mNotificationIcons.getVisibility(); |
| int v = visible ? View.VISIBLE : View.INVISIBLE; |
| if (old != v) { |
| mNotificationIcons.setVisibility(v); |
| mNotificationIcons.startAnimation(loadAnim(anim, null)); |
| } |
| } |
| |
| void updateExpandedInvisiblePosition() { |
| mTrackingPosition = -mDisplayMetrics.heightPixels; |
| } |
| |
| static final float saturate(float a) { |
| return a < 0f ? 0f : (a > 1f ? 1f : a); |
| } |
| |
| @Override |
| protected int getExpandedViewMaxHeight() { |
| return mDisplayMetrics.heightPixels - mNotificationPanelMarginBottomPx; |
| } |
| |
| @Override |
| public void updateExpandedViewPos(int thingy) { |
| if (SPEW) Log.v(TAG, "updateExpandedViewPos"); |
| |
| // on larger devices, the notification panel is propped open a bit |
| mNotificationPanel.setMinimumHeight( |
| (int)(mNotificationPanelMinHeightFrac * mCurrentDisplaySize.y)); |
| |
| FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams(); |
| lp.gravity = mNotificationPanelGravity; |
| lp.setMarginStart(mNotificationPanelMarginPx); |
| mNotificationPanel.setLayoutParams(lp); |
| |
| if (mSettingsPanel != null) { |
| lp = (FrameLayout.LayoutParams) mSettingsPanel.getLayoutParams(); |
| lp.gravity = mSettingsPanelGravity; |
| lp.setMarginEnd(mNotificationPanelMarginPx); |
| mSettingsPanel.setLayoutParams(lp); |
| } |
| |
| if (ENABLE_HEADS_UP && mHeadsUpNotificationView != null) { |
| mHeadsUpNotificationView.setMargin(mNotificationPanelMarginPx); |
| mPile.getLocationOnScreen(mPilePosition); |
| mHeadsUpVerticalOffset = mPilePosition[1] - mNaturalBarHeight; |
| } |
| |
| updateCarrierLabelVisibility(false); |
| } |
| |
| // 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)); |
| } |
| } |
| |
| private View.OnClickListener mClearButtonListener = new View.OnClickListener() { |
| public void onClick(View v) { |
| // TODO: Handle this better with notification stack scroller |
| synchronized (mNotificationData) { |
| mPostCollapseCleanup = new Runnable() { |
| @Override |
| public void run() { |
| if (DEBUG) { |
| Log.v(TAG, "running post-collapse cleanup"); |
| } |
| try { |
| if (!ENABLE_NOTIFICATION_STACK) { |
| ((NotificationRowLayout) mPile).setViewRemoval(true); |
| } |
| mBarService.onClearAllNotifications(mCurrentUserId); |
| } catch (Exception ex) { } |
| } |
| }; |
| |
| if(ENABLE_NOTIFICATION_STACK) { |
| animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); |
| return; |
| } |
| |
| // animate-swipe all dismissable notifications, then animate the shade closed |
| int numChildren = mPile.getChildCount(); |
| |
| int scrollTop = mNotificationScroller.getScrollY(); |
| int scrollBottom = scrollTop + mNotificationScroller.getHeight(); |
| final ArrayList<View> snapshot = new ArrayList<View>(numChildren); |
| for (int i=0; i<numChildren; i++) { |
| final View child = mPile.getChildAt(i); |
| if (((SwipeHelper.Callback) mPile).canChildBeDismissed(child) |
| && child.getBottom() > scrollTop && child.getTop() < scrollBottom) { |
| snapshot.add(child); |
| } |
| } |
| if (snapshot.isEmpty()) { |
| animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); |
| return; |
| } |
| new Thread(new Runnable() { |
| @Override |
| public void run() { |
| // Decrease the delay for every row we animate to give the sense of |
| // accelerating the swipes |
| final int ROW_DELAY_DECREMENT = 10; |
| int currentDelay = 140; |
| int totalDelay = 0; |
| |
| |
| if (!ENABLE_NOTIFICATION_STACK) { |
| // Set the shade-animating state to avoid doing other work during |
| // all of these animations. In particular, avoid layout and |
| // redrawing when collapsing the shade. |
| ((NotificationRowLayout) mPile).setViewRemoval(false); |
| } |
| |
| View sampleView = snapshot.get(0); |
| int width = sampleView.getWidth(); |
| final int dir = sampleView.isLayoutRtl() ? -1 : +1; |
| final int velocity = dir * width * 8; // 1000/8 = 125 ms duration |
| for (final View _v : snapshot) { |
| mHandler.postDelayed(new Runnable() { |
| @Override |
| public void run() { |
| if (!ENABLE_NOTIFICATION_STACK) { |
| ((NotificationRowLayout) mPile).dismissRowAnimated( |
| _v, velocity); |
| } else { |
| ((NotificationStackScrollLayout) mPile).dismissRowAnimated( |
| _v, velocity); |
| } |
| } |
| }, totalDelay); |
| currentDelay = Math.max(50, currentDelay - ROW_DELAY_DECREMENT); |
| totalDelay += currentDelay; |
| } |
| // Delay the collapse animation until after all swipe animations have |
| // finished. Provide some buffer because there may be some extra delay |
| // before actually starting each swipe animation. Ideally, we'd |
| // synchronize the end of those animations with the start of the collaps |
| // exactly. |
| mHandler.postDelayed(new Runnable() { |
| @Override |
| public void run() { |
| animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); |
| } |
| }, totalDelay + 225); |
| } |
| }).start(); |
| } |
| } |
| }; |
| |
| public void startActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned) { |
| if (onlyProvisioned && !isDeviceProvisioned()) return; |
| try { |
| // Dismiss the lock screen when Settings starts. |
| ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); |
| } catch (RemoteException e) { |
| } |
| intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); |
| mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); |
| animateCollapsePanels(); |
| } |
| |
| private View.OnClickListener mSettingsButtonListener = new View.OnClickListener() { |
| public void onClick(View v) { |
| if (mHasSettingsPanel) { |
| animateExpandSettingsPanel(); |
| } else { |
| startActivityDismissingKeyguard( |
| new Intent(android.provider.Settings.ACTION_SETTINGS), true); |
| } |
| } |
| }; |
| |
| private View.OnClickListener mClockClickListener = new View.OnClickListener() { |
| public void onClick(View v) { |
| startActivityDismissingKeyguard( |
| new Intent(Intent.ACTION_QUICK_CLOCK), true); // have fun, everyone |
| } |
| }; |
| |
| private View.OnClickListener mNotificationButtonListener = new View.OnClickListener() { |
| public void onClick(View v) { |
| animateExpandNotificationsPanel(); |
| } |
| }; |
| |
| 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)) { |
| int flags = CommandQueue.FLAG_EXCLUDE_NONE; |
| if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { |
| 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; |
| // no waiting! |
| makeExpandedInvisible(); |
| notifyNavigationBarScreenOn(false); |
| notifyHeadsUpScreenOn(false); |
| finishBarAnimations(); |
| } |
| else if (Intent.ACTION_SCREEN_ON.equals(action)) { |
| mScreenOn = true; |
| // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018) |
| repositionNavigationBar(); |
| 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); |
| } |
| } |
| } |
| } |
| } |
| }; |
| |
| // 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(); |
| updateExpandedViewPos(EXPANDED_LEAVE_ALONE); |
| updateShowSearchHoldoff(); |
| } |
| |
| @Override |
| public void userSwitched(int newUserId) { |
| if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId); |
| animateCollapsePanels(); |
| updateNotificationIcons(); |
| resetUserSetupObserver(); |
| } |
| |
| private void resetUserSetupObserver() { |
| mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver); |
| mUserSetupObserver.onChange(false); |
| mContext.getContentResolver().registerContentObserver( |
| Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true, |
| mUserSetupObserver, |
| mCurrentUserId); |
| } |
| |
| private void setHeadsUpVisibility(boolean vis) { |
| if (!ENABLE_HEADS_UP) return; |
| if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window"); |
| mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE); |
| if (!vis) { |
| if (DEBUG) Log.d(TAG, "setting heads up entry to null"); |
| mInterruptingNotificationEntry = null; |
| } |
| } |
| |
| public void animateHeadsUp(boolean animateInto, float frac) { |
| if (!ENABLE_HEADS_UP || mHeadsUpNotificationView == null) return; |
| frac = frac / 0.4f; |
| frac = frac < 1.0f ? frac : 1.0f; |
| float alpha = 1.0f - frac; |
| float offset = mHeadsUpVerticalOffset * frac; |
| offset = animateInto ? offset : 0f; |
| mHeadsUpNotificationView.setAlpha(alpha); |
| mHeadsUpNotificationView.setY(offset); |
| } |
| |
| public void onHeadsUpDismissed() { |
| if (mInterruptingNotificationEntry == null) return; |
| mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); |
| if (mHeadsUpNotificationView.isClearable()) { |
| try { |
| mBarService.onNotificationClear( |
| mInterruptingNotificationEntry.notification.getPackageName(), |
| mInterruptingNotificationEntry.notification.getTag(), |
| mInterruptingNotificationEntry.notification.getId(), |
| mInterruptingNotificationEntry.notification.getUserId()); |
| } catch (android.os.RemoteException ex) { |
| // oh well |
| } |
| } |
| } |
| |
| /** |
| * 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() { |
| final Context context = mContext; |
| final Resources res = context.getResources(); |
| |
| if (mClearButton instanceof TextView) { |
| ((TextView)mClearButton).setText(context.getText(R.string.status_bar_clear_all_button)); |
| } |
| |
| // Update the QuickSettings container |
| if (mQS != null) mQS.updateResources(); |
| |
| loadDimens(); |
| } |
| |
| protected void loadDimens() { |
| final Resources res = mContext.getResources(); |
| |
| mNaturalBarHeight = res.getDimensionPixelSize( |
| com.android.internal.R.dimen.status_bar_height); |
| |
| int newIconSize = res.getDimensionPixelSize( |
| com.android.internal.R.dimen.status_bar_icon_size); |
| int newIconHPadding = res.getDimensionPixelSize( |
| R.dimen.status_bar_icon_padding); |
| |
| if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) { |
| // Log.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding); |
| mIconHPadding = newIconHPadding; |
| mIconSize = newIconSize; |
| //reloadAllNotificationIcons(); // reload the tray |
| } |
| |
| mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); |
| |
| mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity); |
| mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity); |
| mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity); |
| mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity); |
| |
| mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1); |
| mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1); |
| |
| mExpandAccelPx = res.getDimension(R.dimen.expand_accel); |
| mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel); |
| |
| mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity); |
| |
| mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity); |
| |
| mNotificationPanelMarginBottomPx |
| = (int) res.getDimension(R.dimen.notification_panel_margin_bottom); |
| mNotificationPanelMarginPx |
| = (int) res.getDimension(R.dimen.notification_panel_margin_left); |
| mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity); |
| if (mNotificationPanelGravity <= 0) { |
| mNotificationPanelGravity = Gravity.START | Gravity.TOP; |
| } |
| mSettingsPanelGravity = res.getInteger(R.integer.settings_panel_layout_gravity); |
| Log.d(TAG, "mSettingsPanelGravity = " + mSettingsPanelGravity); |
| if (mSettingsPanelGravity <= 0) { |
| mSettingsPanelGravity = Gravity.END | Gravity.TOP; |
| } |
| |
| mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height); |
| mNotificationHeaderHeight = res.getDimensionPixelSize(R.dimen.notification_panel_header_height); |
| |
| mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1); |
| if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) { |
| mNotificationPanelMinHeightFrac = 0f; |
| } |
| |
| mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay); |
| mRowHeight = res.getDimensionPixelSize(R.dimen.notification_row_min_height); |
| |
| if (false) Log.v(TAG, "updateResources"); |
| } |
| |
| // |
| // tracing |
| // |
| |
| void postStartTracing() { |
| mHandler.postDelayed(mStartTracing, 3000); |
| } |
| |
| void vibrate() { |
| android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService( |
| Context.VIBRATOR_SERVICE); |
| vib.vibrate(250, AudioManager.STREAM_SYSTEM); |
| } |
| |
| 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 |
| protected void haltTicker() { |
| mTicker.halt(); |
| } |
| |
| @Override |
| protected boolean shouldDisableNavbarGestures() { |
| return !isDeviceProvisioned() |
| || mExpandedVisible |
| || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0; |
| } |
| |
| public void startSettingsActivity(String action) { |
| if (mQS != null) { |
| mQS.startSettingsActivity(action); |
| } |
| } |
| |
| 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 cf) { |
| } |
| |
| @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; |
| } |
| mContext.unregisterReceiver(mBroadcastReceiver); |
| } |
| |
| private boolean mDemoModeAllowed; |
| private boolean mDemoMode; |
| private DemoStatusIcons mDemoStatusIcons; |
| |
| @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_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)) { |
| if (mDemoStatusIcons == null) { |
| mDemoStatusIcons = new DemoStatusIcons(mStatusIcons, mIconSize); |
| } |
| mDemoStatusIcons.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 : |
| -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); |
| } |
| } |
| } |