/* * 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.NAVIGATION_HINT_IME_SHOWN; import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.StatusBarManager.windowStateToString; import static com.android.keyguard.KeyguardHostView.OnDismissAction; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; 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.provider.Settings.Global; import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.view.Display; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; 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.ViewTreeObserver; 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.keyguard.ViewMediatorCallback; import com.android.systemui.DemoMode; import com.android.systemui.EventLogTags; import com.android.systemui.R; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.qs.CircularClipper; import com.android.systemui.qs.QSPanel; import com.android.systemui.qs.QSTile; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.InterceptedNotifications; import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.NotificationData.Entry; import com.android.systemui.statusbar.NotificationOverflowContainer; import com.android.systemui.statusbar.SignalClusterView; import com.android.systemui.statusbar.SpeedBumpView; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BluetoothControllerImpl; import com.android.systemui.statusbar.policy.CastControllerImpl; import com.android.systemui.statusbar.policy.DateView; import com.android.systemui.statusbar.policy.HeadsUpNotificationView; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.LocationControllerImpl; import com.android.systemui.statusbar.policy.NetworkControllerImpl; import com.android.systemui.statusbar.policy.RotationLockControllerImpl; import com.android.systemui.statusbar.policy.ZenModeControllerImpl; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener; import com.android.systemui.statusbar.stack.StackScrollState.ViewState; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; public class PhoneStatusBar extends BaseStatusBar implements DemoMode, DragDownHelper.OnDragDownListener, ActivityStarter { 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; /** * Default value of {@link android.provider.Settings.Global#LOCK_SCREEN_SHOW_NOTIFICATIONS}. */ private static final boolean ALLOW_NOTIFICATIONS_DEFAULT = false; private static final int STATUS_OR_NAV_TRANSIENT = View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT; private static final long AUTOHIDE_TIMEOUT_MS = 3000; /** The minimum delay in ms between reports of notification visibility. */ private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500; // 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 BluetoothControllerImpl mBluetoothController; BatteryController mBatteryController; LocationControllerImpl mLocationController; NetworkControllerImpl mNetworkController; RotationLockControllerImpl mRotationLockController; UserInfoController mUserInfoController; ZenModeControllerImpl mZenModeController; CastControllerImpl mCastController; int mNaturalBarHeight = -1; int mIconSize = -1; int mIconHPadding = -1; Display mDisplay; Point mCurrentDisplaySize = new Point(); StatusBarWindowView mStatusBarWindow; PhoneStatusBarView mStatusBarView; private int mStatusBarWindowState = WINDOW_STATE_SHOWING; private StatusBarWindowManager mStatusBarWindowManager; int mPixelFormat; Object mQueueLock = new Object(); // viewgroup containing the normal contents of the statusbar LinearLayout mStatusBarContents; // right-hand icons LinearLayout mSystemIconArea; LinearLayout mSystemIcons; // left-hand icons LinearLayout mStatusIcons; // the icons themselves IconMerger mNotificationIcons; // [+> View mMoreIcon; // expanded notifications NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window View mExpandedContents; int mNotificationPanelGravity; int mNotificationPanelMarginBottomPx; float mNotificationPanelMinHeightFrac; TextView mNotificationPanelDebugText; // settings View mFlipSettingsView; private QSPanel mQSPanel; // top bar StatusBarHeaderView mHeader; View mKeyguardStatusView; KeyguardBottomAreaView mKeyguardBottomArea; boolean mLeaveOpenOnKeyguardHide; KeyguardIndicationTextView mKeyguardIndicationTextView; // TODO: Fetch phrase from search/hotword provider. String mKeyguardHotwordPhrase = ""; int mKeyguardMaxNotificationCount; View mDateTimeView; // carrier/wifi label private TextView mCarrierLabel; private boolean mCarrierLabelVisible = false; private int mCarrierLabelHeight; private TextView mEmergencyCallLabel; private int mStatusBarHeaderHeight; 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 (userSetup != mUserSetup) { mUserSetup = userSetup; if (mNotificationPanel != null) { mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned() && 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 StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private ViewMediatorCallback mKeyguardViewMediatorCallback; private ScrimController mScrimController; private final Runnable mAutohide = new Runnable() { @Override public void run() { int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT; if (mSystemUiVisibility != requested) { notifyUiVisibilityChanged(requested); } }}; private Runnable mOnFlipRunnable; private InterceptedNotifications mIntercepted; private VelocityTracker mSettingsTracker; private float mSettingsDownY; private boolean mSettingsStarted; private boolean mSettingsCancelled; private boolean mSettingsClosing; private boolean mVisible; private final OnChildLocationsChangedListener mOnChildLocationsChangedListener = new OnChildLocationsChangedListener() { @Override public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) { userActivity(); } }; public void setOnFlipRunnable(Runnable onFlipRunnable) { mOnFlipRunnable = onFlipRunnable; } /** Keys of notifications currently visible to the user. */ private final ArraySet mCurrentlyVisibleNotifications = new ArraySet(); private long mLastVisibilityReportUptimeMs; private static final int VISIBLE_LOCATIONS = ViewState.LOCATION_FIRST_CARD | ViewState.LOCATION_TOP_STACK_PEEKING | ViewState.LOCATION_MAIN_AREA | ViewState.LOCATION_BOTTOM_STACK_PEEKING; private final OnChildLocationsChangedListener mNotificationLocationsChangedListener = new OnChildLocationsChangedListener() { @Override public void onChildLocationsChanged( NotificationStackScrollLayout stackScrollLayout) { if (mHandler.hasCallbacks(mVisibilityReporter)) { // Visibilities will be reported when the existing // callback is executed. return; } // Calculate when we're allowed to run the visibility // reporter. Note that this timestamp might already have // passed. That's OK, the callback will just be executed // ASAP. long nextReportUptimeMs = mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS; mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs); } }; // Tracks notifications currently visible in mNotificationStackScroller and // emits visibility events via NoMan on changes. private final Runnable mVisibilityReporter = new Runnable() { private final ArrayList mTmpNewlyVisibleNotifications = new ArrayList(); private final ArrayList mTmpCurrentlyVisibleNotifications = new ArrayList(); @Override public void run() { mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis(); // 1. Loop over mNotificationData entries: // A. Keep list of visible notifications. // B. Keep list of previously hidden, now visible notifications. // 2. Compute no-longer visible notifications by removing currently // visible notifications from the set of previously visible // notifications. // 3. Report newly visible and no-longer visible notifications. // 4. Keep currently visible notifications for next report. int N = mNotificationData.size(); for (int i = 0; i < N; i++) { Entry entry = mNotificationData.get(i); String key = entry.notification.getKey(); boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(key); boolean currentlyVisible = (mStackScroller.getChildLocation(entry.row) & VISIBLE_LOCATIONS) != 0; if (currentlyVisible) { // Build new set of visible notifications. mTmpCurrentlyVisibleNotifications.add(key); } if (!previouslyVisible && currentlyVisible) { mTmpNewlyVisibleNotifications.add(key); } } ArraySet noLongerVisibleNotifications = mCurrentlyVisibleNotifications; noLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications); logNotificationVisibilityChanges( mTmpNewlyVisibleNotifications, noLongerVisibleNotifications); mCurrentlyVisibleNotifications.clear(); mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications); mTmpNewlyVisibleNotifications.clear(); mTmpCurrentlyVisibleNotifications.clear(); } }; private final View.OnClickListener mOverflowClickListener = new View.OnClickListener() { @Override public void onClick(View v) { goToLockedShade(null); } }; @Override public void setZenMode(int mode) { super.setZenMode(mode); if (!isDeviceProvisioned()) return; final boolean zen = mode != Settings.Global.ZEN_MODE_OFF; if (!zen) { mIntercepted.releaseIntercepted(); } if (mIconPolicy != null) { mIconPolicy.setZenMode(zen); } } @Override public void start() { mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay(); updateDisplaySize(); mIntercepted = new InterceptedNotifications(mContext, this); 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); } startKeyguard(); } // ================================================================================ // 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); // 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); mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons); mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons); mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons); mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon); mNotificationIcons.setOverflowIndicator(mMoreIcon); mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents); mTickerView = mStatusBarView.findViewById(R.id.ticker); mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById( R.id.notification_stack_scroller); mStackScroller.setLongPressListener(getNotificationLongClicker()); mStackScroller.setChildLocationsChangedListener(mOnChildLocationsChangedListener); mKeyguardIconOverflowContainer = (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate( R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false); mKeyguardIconOverflowContainer.setOnActivatedListener(this); mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener); mStackScroller.addView(mKeyguardIconOverflowContainer); SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate( R.layout.status_bar_notification_speed_bump, mStackScroller, false); mStackScroller.setSpeedBumpView(speedBump); mExpandedContents = mStackScroller; mScrimController = new ScrimController(mStatusBarWindow.findViewById(R.id.scrim_behind), mStatusBarWindow.findViewById(R.id.scrim_in_front)); mStatusBarView.setScrimController(mScrimController); mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header); mHeader.setActivityStarter(this); mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view); mKeyguardBottomArea = (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area); mKeyguardBottomArea.setActivityStarter(this); mKeyguardIndicationTextView = (KeyguardIndicationTextView) mStatusBarWindow.findViewById( R.id.keyguard_indication_text); mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date); mDateTimeView = mHeader.findViewById(R.id.datetime); if (mDateTimeView != null) { mDateTimeView.setOnClickListener(mClockClickListener); mDateTimeView.setEnabled(true); } 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 LocationControllerImpl(mContext); // will post a notification mBatteryController = new BatteryController(mContext); mNetworkController = new NetworkControllerImpl(mContext); mBluetoothController = new BluetoothControllerImpl(mContext); if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) { mRotationLockController = new RotationLockControllerImpl(mContext); } mUserInfoController = new UserInfoController(mContext); mZenModeController = new ZenModeControllerImpl(mContext, mHandler); mCastController = new CastControllerImpl(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); // TODO: Uncomment when correctly positioned // 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 // TODO: uncomment, handle this for the Stack scroller aswell // ((NotificationRowLayout) mStackScroller) // .setOnSizeChangedListener(new OnSizeChangedListener() { // @Override // public void onSizeChanged(View view, int w, int h, int oldw, int oldh) { // updateCarrierLabelVisibility(false); } // Set up the quick settings tile panel mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel); if (mQSPanel != null) { mQSPanel.setUtils(new CircularClipper.Utils() { @Override public ValueAnimator createRevealAnimator(View v, int centerX, int centerY, float startRadius, float endRadius) { return v.createRevealAnimator(centerX, centerY, startRadius, endRadius); } }); final QSTileHost qsh = new QSTileHost(mContext, this, mBluetoothController, mLocationController, mRotationLockController, mNetworkController, mZenModeController, null /*tethering*/, mCastController); for (QSTile tile : qsh.getTiles()) { mQSPanel.addTile(tile); } mHeader.setQSPanel(mQSPanel); } // User info. Trigger first load. mHeader.setUserInfoController(mUserInfoController); mUserInfoController.reloadUserInfo(); 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; } private void startKeyguard() { KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class); mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this, mStatusBarWindow, mStatusBarWindowManager, mScrimController); mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback(); } @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); } } 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 UserHandle getCurrentUserHandle() { return new UserHandle(mCurrentUserId); } @Override public void addNotificationInternal(StatusBarNotification notification) { if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore()); Entry shadeEntry = createNotificationViews(notification); if (shadeEntry == null) { return; } if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification)) { return; } if (mUseHeadsUp && shouldInterrupt(notification)) { if (DEBUG) Log.d(TAG, "launching notification in heads up mode"); Entry interruptionCandidate = new Entry(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(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); } } @Override public void updateNotification(StatusBarNotification notification) { super.updateNotification(notification); mIntercepted.update(notification); } @Override public void removeNotificationInternal(String 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() && mState != StatusBarState.KEYGUARD) { animateCollapsePanels(); } } mIntercepted.remove(key); setAreThereNotifications(); } @Override protected void refreshLayout(int layoutDirection) { if (mNavigationBarView != null) { mNavigationBarView.setLayoutDirection(layoutDirection); } refreshAllStatusBarIcons(); } private void updateShowSearchHoldoff() { mShowSearchHoldoff = mContext.getResources().getInteger( R.integer.config_show_search_delay); } private void loadNotificationShade() { if (mStackScroller == null) return; int N = mNotificationData.size(); ArrayList toShow = new ArrayList(); final boolean provisioned = isDeviceProvisioned(); // If the device hasn't been through Setup, we only show system notifications for (int i=0; i toRemove = new ArrayList(); for (int i=0; i< mStackScroller.getChildCount(); i++) { View child = mStackScroller.getChildAt(i); if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) { toRemove.add(child); } } for (View remove : toRemove) { mStackScroller.removeView(remove); } for (int i=0; i