/* * 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; import android.app.ActivityManagerNative; import android.app.Dialog; import android.app.Notification; import android.app.PendingIntent; import android.app.Service; import android.app.StatusBarManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.IPowerManager; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.util.Slog; 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.View.OnLongClickListener; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.view.WindowManagerImpl; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RemoteViews; import android.widget.ScrollView; import android.widget.TextView; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; import com.android.internal.statusbar.StatusBarNotification; import com.android.systemui.R; import com.android.systemui.statusbar.policy.StatusBarPolicy; import com.android.systemui.statusbar.quickpanel.QuickSettingsView; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; public class StatusBarService extends Service implements CommandQueue.Callbacks { static final String TAG = "StatusBarService"; static final boolean SPEW_ICONS = false; static final boolean SPEW = false; public static final String ACTION_STATUSBAR_START = "com.android.internal.policy.statusbar.START"; static final int EXPANDED_LEAVE_ALONE = -10000; static final int EXPANDED_FULL_OPEN = -10001; private static final int MSG_ANIMATE = 1000; private static final int MSG_ANIMATE_REVEAL = 1001; private static final int MSG_SAMSUNG_MAGIC = 2000; private int mIsBrightNessMode = 0; private boolean mIsStatusBarBrightNess; private boolean mIsAutoBrightNess; private BrightNessContentObserver mBrightNessContentObs = new BrightNessContentObserver(); StatusBarPolicy mIconPolicy; CommandQueue mCommandQueue; IStatusBarService mBarService; int mIconSize; Display mDisplay; StatusBarView mStatusBarView; int mPixelFormat; H mHandler = new H(); Object mQueueLock = new Object(); // icons LinearLayout mIcons; IconMerger mNotificationIcons; LinearLayout mStatusIcons; // expanded notifications Dialog mExpandedDialog; ExpandedView mExpandedView; WindowManager.LayoutParams mExpandedParams; ScrollView mScrollView; View mNotificationLinearLayout; View mExpandedContents; // top bar TextView mNoNotificationsTitle; TextView mClearButton; // drag bar CloseDragHandle mCloseView; // ongoing NotificationData mOngoing = new NotificationData(); TextView mOngoingTitle; LinearLayout mOngoingItems; // latest NotificationData mLatest = new NotificationData(); TextView mLatestTitle; LinearLayout mLatestItems; // position int[] mPositionTmp = new int[2]; boolean mExpanded; boolean mExpandedVisible; // the date view DateView mDateView; // the tracker view TrackingView mTrackingView; WindowManager.LayoutParams mTrackingParams; int mTrackingPosition; // the position of the top of the tracking view. private boolean mPanelSlightlyVisible; // 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; static final int ANIM_FRAME_DURATION = (1000 / 60); boolean mAnimating; long mCurAnimationTime; float mDisplayHeight; float mAnimY; float mAnimVel; float mAnimAccel; long mAnimLastTime; boolean mAnimatingReveal = false; int mViewDelta; int[] mAbsPos = new int[2]; // for disabling the status bar int mDisabled = 0; LinearLayout mMiniCon; NotificationData mMiniConData = new NotificationData(); private Context mContext; // music MusicControls mMusicControls; public static ImageView mMusicToggleButton; boolean swipeToClearNotifications = true; boolean longPressToClearNotifications = false; boolean useAlternativeToggleLayout = false; private class ExpandedDialog extends Dialog { ExpandedDialog(Context context) { super(context, com.android.internal.R.style.Theme_Light_NoTitleBar); } @Override public boolean dispatchKeyEvent(KeyEvent event) { boolean down = event.getAction() == KeyEvent.ACTION_DOWN; switch (event.getKeyCode()) { case KeyEvent.KEYCODE_BACK: if (!down) { animateCollapse(); } return true; } return super.dispatchKeyEvent(event); } } @Override public void onCreate() { // First set up our views and stuff. mDisplay = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); makeStatusBarView(this); // Connect in to the status bar manager service StatusBarIconList iconList = new StatusBarIconList(); ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>(); ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>(); mCommandQueue = new CommandQueue(this, iconList); mBarService = IStatusBarService.Stub.asInterface(ServiceManager .getService(Context.STATUS_BAR_SERVICE)); try { mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications); } catch (RemoteException ex) { // If the system process isn't there we're doomed anyway. } // Set up the initial icon state int N = iconList.size(); int viewIndex = 0; for (int i = 0; i < N; i++) { StatusBarIcon icon = iconList.getIcon(i); if (icon != null) { addIcon(iconList.getSlot(i), i, viewIndex, icon); viewIndex++; } } // Set up the initial notification state N = notificationKeys.size(); if (N == notifications.size()) { for (int i = 0; i < N; i++) { addNotification(notificationKeys.get(i), notifications.get(i)); } } else { Log.wtf(TAG, "Notification list length mismatch: keys=" + N + " notifications=" + notifications.size()); } // Put up the view addStatusBarView(); // Lastly, call to the icon policy to install/update all the icons. mIconPolicy = new StatusBarPolicy(this); mIsStatusBarBrightNess = getResources().getBoolean(R.bool.config_status_bar_brightness); if (mIsStatusBarBrightNess) { mIsAutoBrightNess = checkAutoBrightNess(); getContentResolver().registerContentObserver( Settings.System.getUriFor("screen_brightness_mode"), false, mBrightNessContentObs); updatePropFactorValue(); } mContext = getApplicationContext(); SettingsObserver settingsObserver = new SettingsObserver(mHandler); settingsObserver.observe(); } @Override public void onDestroy() { // we're never destroyed } /** * Nobody binds to us. */ @Override public IBinder onBind(Intent intent) { return null; } // ================================================================================ // Constructing the view // ================================================================================ private void makeStatusBarView(Context context) { Resources res = context.getResources(); mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size); ExpandedView expanded = (ExpandedView) View.inflate(context, R.layout.status_bar_expanded, null); expanded.mService = this; LinearLayout page2 = (LinearLayout) expanded.findViewById(R.id.page2); StatusBarView sb = (StatusBarView) View.inflate(context, R.layout.status_bar, null); sb.mService = this; // figure out which pixel-format to use for the status bar. mPixelFormat = PixelFormat.TRANSLUCENT; Drawable bg = sb.getBackground(); if (bg != null) { mPixelFormat = bg.getOpacity(); } mStatusBarView = sb; mStatusIcons = (LinearLayout) sb.findViewById(R.id.statusIcons); mNotificationIcons = (IconMerger) sb.findViewById(R.id.notificationIcons); mIcons = (LinearLayout) sb.findViewById(R.id.icons); mTickerView = sb.findViewById(R.id.ticker); mDateView = (DateView) sb.findViewById(R.id.date); mExpandedDialog = new ExpandedDialog(context); mExpandedView = expanded; mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout); mOngoingTitle = (TextView) expanded.findViewById(R.id.ongoingTitle); mOngoingItems = (LinearLayout) expanded.findViewById(R.id.ongoingItems); mLatestTitle = (TextView) expanded.findViewById(R.id.latestTitle); mLatestItems = (LinearLayout) expanded.findViewById(R.id.latestItems); mNoNotificationsTitle = (TextView) expanded.findViewById(R.id.noNotificationsTitle); mClearButton = (TextView) expanded.findViewById(R.id.clear_all_button); mClearButton.setOnClickListener(mClearButtonListener); mScrollView = (ScrollView) expanded.findViewById(R.id.scroll); mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout); mExpandedView.setVisibility(View.GONE); mOngoingTitle.setVisibility(View.GONE); mLatestTitle.setVisibility(View.GONE); mTicker = new MyTicker(context, sb); TickerView tickerView = (TickerView) sb.findViewById(R.id.tickerText); tickerView.mTicker = mTicker; mTrackingView = (TrackingView) View.inflate(context, R.layout.status_bar_tracking, null); mTrackingView.mService = this; mCloseView = (CloseDragHandle) mTrackingView.findViewById(R.id.close); mCloseView.mService = this; mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); // set the inital view visibility setAreThereNotifications(); mDateView.setVisibility(View.INVISIBLE); // receive broadcasts IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); filter.addAction(Intent.ACTION_SCREEN_OFF); context.registerReceiver(mBroadcastReceiver, filter); if (useAlternativeToggleLayout) { QuickSettingsView qsv = (QuickSettingsView) View.inflate(context, R.layout.quickpanel_quick_settings_page, null); page2.addView(qsv, 0); } else { QuickSettingsView qsv = (QuickSettingsView) View.inflate(context, R.layout.quickpanel_quick_settings, null); expanded.addView(qsv, 0); } // if (useCustomMusic) { // music mMusicToggleButton = (ImageView) expanded.findViewById(R.id.music_toggle_button); mMusicToggleButton.setOnClickListener(mMusicToggleButtonListener); mMusicControls = (MusicControls) expanded.findViewById(R.id.exp_music_controls); // } else { mMiniCon = new LinearLayout(context); mMiniCon.setOrientation(LinearLayout.VERTICAL); useCustomMusic = Settings.System.getInt(getContentResolver(), "tweaks_use_custom_music_controls", 1) == 1; // if (!useCustomMusic) { // ((LinearLayout) // mExpandedView.findViewById(R.id.notificationLinearLayout)).addView( // mMiniCon, 0); // // mMusicToggleButton.setVisibility(View.GONE); // mMusicControls.disable(); // } mCallWidget = (CallWidget) expanded.findViewById(R.id.call_widget); mCallOnGoingView = (CallOnGoingView) View.inflate(context, R.layout.status_bar_call_ongoing, null); mCallOnGoingView.mService = this; } public CallWidget mCallWidget; boolean useCustomMusic = true; protected void addStatusBarView() { Resources res = getResources(); final int height = res .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); final StatusBarView view = mStatusBarView; 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, PixelFormat.TRANSLUCENT); lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; lp.setTitle("StatusBar"); lp.windowAnimations = com.android.internal.R.style.Animation_StatusBar; mStatusBarView.setVisibility(View.GONE); WindowManagerImpl.getDefault().addView(view, lp); mMusicControls.setupControls(); } public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) { if (SPEW_ICONS) { Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex + " icon=" + icon); } StatusBarIconView view = new StatusBarIconView(this, slot); 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_ICONS) { Slog.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_ICONS) { Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex); } mStatusIcons.removeViewAt(viewIndex); } public void addNotification(IBinder key, StatusBarNotification notification) { boolean shouldTick = true; if (notification.notification.fullScreenIntent != null) { shouldTick = false; Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent"); try { notification.notification.fullScreenIntent.send(); } catch (PendingIntent.CanceledException e) { } } StatusBarIconView iconView = addNotificationViews(key, notification); if (iconView == null) return; if (shouldTick) { tick(notification); } // Recalculate the position of the sliding windows and the titles. setAreThereNotifications(); updateExpandedViewPos(EXPANDED_LEAVE_ALONE); } public void updateNotification(IBinder key, StatusBarNotification notification) { NotificationData oldList; int oldIndex = mOngoing.findEntry(key); if (oldIndex >= 0) { oldList = mOngoing; } else { oldIndex = mLatest.findEntry(key); if (oldIndex >= 0) { oldList = mLatest; } else { oldIndex = mMiniConData.findEntry(key); if (oldIndex < 0) { Slog.w(TAG, "updateNotification for unknown key: " + key); return; } else { oldList = mMiniConData; } } } final NotificationData.Entry oldEntry = oldList.getEntryAt(oldIndex); final StatusBarNotification oldNotification = oldEntry.notification; final RemoteViews oldContentView = oldNotification.notification.contentView; final RemoteViews contentView = notification.notification.contentView; if (false) { Slog.d(TAG, "old notification: when=" + oldNotification.notification.when + " ongoing=" + oldNotification.isOngoing() + " expanded=" + oldEntry.expanded + " contentView=" + oldContentView); Slog.d(TAG, "new notification: when=" + notification.notification.when + " ongoing=" + oldNotification.isOngoing() + " contentView=" + contentView); } // Can we just reapply the RemoteViews in place? If when didn't change, // the order // didn't change. if (notification.notification.when == oldNotification.notification.when && notification.isOngoing() == oldNotification.isOngoing() && oldEntry.expanded != null && contentView != null && oldContentView != null && contentView.getPackage() != null && oldContentView.getPackage() != null && oldContentView.getPackage().equals(contentView.getPackage()) && oldContentView.getLayoutId() == contentView.getLayoutId()) { if (SPEW) Slog.d(TAG, "reusing notification"); oldEntry.notification = notification; try { if (!notification.isMiniCon()) { // Reapply the RemoteViews contentView.reapply(this, oldEntry.content); } else { // Reapply the RemoteViews contentView.reapply(this, oldEntry.expanded); Slog.i(TAG, "UPDATE:MiniCon-" + notification.notification.twQuickPanelEvent); } // update the contentIntent final PendingIntent contentIntent = notification.notification.contentIntent; if (contentIntent != null) { oldEntry.content.setOnClickListener(new Launcher(contentIntent, notification.pkg, notification.tag, notification.id)); } // Update the icon. final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon, notification.notification.iconLevel, notification.notification.number); if (!oldEntry.icon.set(ic)) { handleNotificationError(key, notification, "Couldn't update icon: " + ic); return; } } catch (RuntimeException e) { // It failed to add cleanly. Log, and remove the view from the // panel. Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e); removeNotificationViews(key); addNotificationViews(key, notification); } } else { if (SPEW) Slog.d(TAG, "not reusing notification"); removeNotificationViews(key); addNotificationViews(key, notification); } // Restart the ticker if it's still running if (notification.notification.tickerText != null && !TextUtils.equals(notification.notification.tickerText, oldEntry.notification.notification.tickerText)) { tick(notification); } // Recalculate the position of the sliding windows and the titles. setAreThereNotifications(); updateExpandedViewPos(EXPANDED_LEAVE_ALONE); } public void removeNotification(IBinder key) { if (SPEW) Slog.d(TAG, "removeNotification key=" + key); StatusBarNotification old = removeNotificationViews(key); if (old != null) { // Cancel the ticker if it's still running // mTicker.removeEntry(old); // Recalculate the position of the sliding windows and the titles. setAreThereNotifications(); updateExpandedViewPos(EXPANDED_LEAVE_ALONE); } } private int chooseIconIndex(boolean isMiniCon, boolean isOngoing, int viewIndex) { final int latestSize = mLatest.size(); final int ongoingSize = mOngoing.size(); if (isMiniCon) { return (latestSize + ongoingSize) + (mMiniConData.size() - viewIndex); } else { if (isOngoing) { return latestSize + (ongoingSize - viewIndex); } else { return latestSize - viewIndex; } } } private int chooseIconIndex(boolean isOngoing, int viewIndex) { final int latestSize = mLatest.size(); if (isOngoing) { return latestSize + (mOngoing.size() - viewIndex); } else { return latestSize - viewIndex; } } View[] makeNotificationView(final StatusBarNotification notification, ViewGroup parent) { Notification n = notification.notification; RemoteViews remoteViews = n.contentView; if (remoteViews == null) { return null; } // create the row view LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); // View row = inflater.inflate(R.layout.status_bar_latest_event, parent, // false); LatestItemContainer row = (LatestItemContainer) inflater.inflate( R.layout.status_bar_latest_event, parent, false); if ((n.flags & Notification.FLAG_ONGOING_EVENT) == 0 && (n.flags & Notification.FLAG_NO_CLEAR) == 0) { if (swipeToClearNotifications) row.setOnSwipeCallback(new Runnable() { public void run() { try { mBarService.onNotificationClear(notification.pkg, notification.tag, notification.id); } catch (RemoteException e) { // Skip it, don't crash. } } }); } // bind the click event to the content area ViewGroup content = (ViewGroup) row.findViewById(R.id.content); content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); content.setOnFocusChangeListener(mFocusChangeListener); PendingIntent contentIntent = n.contentIntent; if (contentIntent != null) { content.setOnClickListener(new Launcher(contentIntent, notification.pkg, notification.tag, notification.id)); } if (longPressToClearNotifications) content.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { try { mBarService.onNotificationClear(notification.pkg, notification.tag, notification.id); } catch (RemoteException e) { } return true; } }); View expanded = null; Exception exception = null; try { expanded = remoteViews.apply(this, content); } catch (RuntimeException e) { exception = e; } if (expanded == null) { String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id); Slog.e(TAG, "couldn't inflate view for notification " + ident, exception); return null; } else { content.addView(expanded); row.setDrawingCacheEnabled(true); } return new View[] { row, content, expanded }; } StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) { boolean isOngoing = notification.isOngoing(); NotificationData list; LinearLayout parent; boolean isMiniCon = notification.isMiniCon(); if (isMiniCon) { list = mMiniConData; parent = mMiniCon; } else if (isOngoing) { list = mOngoing; parent = mOngoingItems; } else { list = mLatest; parent = mLatestItems; } // Construct the expanded view. final View[] views = makeNotificationView(notification, parent); if (views == null) { handleNotificationError(key, notification, "Couldn't expand RemoteViews for: " + notification); return null; } final View row = views[0]; final View content = views[1]; final View expanded = views[2]; // Construct the icon. final StatusBarIconView iconView = new StatusBarIconView(this, notification.pkg + "/0x" + Integer.toHexString(notification.id)); final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon, notification.notification.iconLevel, notification.notification.number); if (!iconView.set(ic)) { handleNotificationError(key, notification, "Coulding create icon: " + ic); return null; } // Add the expanded view. final int viewIndex = list.add(key, notification, row, content, expanded, iconView); if (isMiniCon) { ((ViewGroup) content).removeView(expanded); parent.addView(expanded, viewIndex); Slog.i(TAG, "ADD:MiniCon-" + notification.notification.twQuickPanelEvent); } else { parent.addView(row, viewIndex); } // Add the icon. // final int iconIndex = ; mNotificationIcons.addView(iconView, chooseIconIndex(isMiniCon, isOngoing, viewIndex)); return iconView; } StatusBarNotification removeNotificationViews(IBinder key) { NotificationData.Entry entry = mOngoing.remove(key); if (entry == null) { entry = mLatest.remove(key); if (entry == null) { entry = mMiniConData.remove(key); if (entry == null) { Slog.w(TAG, "removeNotification for unknown key: " + key); return null; } } } // cond_34 if (!entry.notification.isMiniCon()) { ((ViewGroup) entry.row.getParent()).removeView(entry.row); } else { // performCollapse(); ((ViewGroup) entry.expanded.getParent()).removeView(entry.expanded); Slog.i(TAG, "REMOVE:MiniCon-" + entry.notification.notification.twQuickPanelEvent); } // Remove the icon. ((ViewGroup) entry.icon.getParent()).removeView(entry.icon); return entry.notification; } private void setAreThereNotifications() { boolean ongoing = mOngoing.hasVisibleItems(); boolean latest = mLatest.hasVisibleItems(); // (no ongoing notifications are clearable) if (mLatest.hasClearableItems()) { mClearButton.setVisibility(View.VISIBLE); } else { mClearButton.setVisibility(View.GONE); } mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE); mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE); if (ongoing || latest) { mNoNotificationsTitle.setVisibility(View.GONE); } else { mNoNotificationsTitle.setVisibility(View.VISIBLE); } } /** * 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 ((diff & StatusBarManager.DISABLE_EXPAND) != 0) { if ((state & StatusBarManager.DISABLE_EXPAND) != 0) { if (SPEW) Slog.d(TAG, "DISABLE_EXPAND: yes"); animateCollapse(); } } if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes"); if (mTicking) { mTicker.halt(); } else { setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out); } } else { if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no"); if (!mExpandedVisible) { setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); } } } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes"); mTicker.halt(); } } } /** * All changes to the status bar and notifications funnel through here and * are batched. */ private class H extends Handler { public void handleMessage(Message m) { switch (m.what) { case MSG_ANIMATE: doAnimation(); break; case MSG_ANIMATE_REVEAL: doRevealAnimation(); break; case MSG_SAMSUNG_MAGIC: if (mIsStatusBarBrightNess) { mIsBrightNessMode = 1; updateExpandedViewPos(0); performCollapse(); } break; } } } 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); } }; private void makeExpandedVisible() { if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); if (mExpandedVisible) { return; } mExpandedVisible = true; visibilityChanged(true); mMusicControls.updateControls(); mMusicControls.setProperVisibility(); mCallWidget.updateWidget(); updateExpandedViewPos(EXPANDED_LEAVE_ALONE); mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; mExpandedDialog.getWindow().setAttributes(mExpandedParams); mExpandedView.requestFocus(View.FOCUS_FORWARD); mTrackingView.setVisibility(View.VISIBLE); mExpandedView.setVisibility(View.VISIBLE); if (!mTicking) { setDateViewVisibility(true, com.android.internal.R.anim.fade_in); } } public void animateExpand() { if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded); if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { return; } if (mExpanded) { return; } prepareTracking(0, true); performFling(0, 2000.0f, true); } public void animateCollapse() { if (SPEW) { Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded + " mExpandedVisible=" + mExpandedVisible + " mExpanded=" + mExpanded + " mAnimating=" + mAnimating + " mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel); } if (!mExpandedVisible) { if (mExpandedParams != null) { mExpandedParams.y = -0x8ae; mExpandedDialog.getWindow().setAttributes(mExpandedParams); } return; } int y; if (mAnimating) { y = (int) mAnimY; } else { y = mDisplay.getHeight() - 1; } // Let the fling think that we're open so it goes in the right direction // and doesn't try to re-open the windowshade. mExpanded = true; prepareTracking(y, false); performFling(y, -2000.0f, true); } void performExpand() { if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded); if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { return; } if (mExpanded) { return; } mExpanded = true; makeExpandedVisible(); updateExpandedViewPos(EXPANDED_FULL_OPEN); // if (false) // postStartTracing(); } void performCollapse() { if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded + " mExpandedVisible=" + mExpandedVisible + " mTicking=" + mTicking); if (!mExpandedVisible) { return; } mExpandedVisible = false; visibilityChanged(false); mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; mExpandedDialog.getWindow().setAttributes(mExpandedParams); mTrackingView.setVisibility(View.GONE); mExpandedView.setVisibility(View.GONE); if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) { setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); } if (mDateView.getVisibility() == View.VISIBLE) { setDateViewVisibility(false, com.android.internal.R.anim.fade_out); } if (!mExpanded) { return; } mExpanded = false; } void doAnimation() { if (mAnimating) { if (SPEW) Slog.d(TAG, "doAnimation"); if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY); incrementAnim(); if (SPEW) Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY); if (mAnimY >= mDisplay.getHeight() - 1) { if (SPEW) Slog.d(TAG, "Animation completed to expanded state."); mAnimating = false; updateExpandedViewPos(EXPANDED_FULL_OPEN); performExpand(); } else if (mAnimY < mStatusBarView.getHeight()) { if (SPEW) Slog.d(TAG, "Animation completed to collapsed state."); mAnimating = false; updateExpandedViewPos(0); performCollapse(); } else { updateExpandedViewPos((int) mAnimY); mCurAnimationTime += ANIM_FRAME_DURATION; mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime); } } } void stopTracking() { mTracking = false; mVelocityTracker.recycle(); mVelocityTracker = null; } void incrementAnim() { long now = SystemClock.uptimeMillis(); float t = ((float) (now - mAnimLastTime)) / 1000; // ms -> s final float y = mAnimY; final float v = mAnimVel; // px/s final float a = mAnimAccel; // px/s/s mAnimY = y + (v * t) + (0.5f * a * t * t); // px mAnimVel = v + (a * t); // px/s mAnimLastTime = now; // ms // Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" // + mAnimY // + " mAnimAccel=" + mAnimAccel); } public void hideCallOnGoingView() { // mStatusBarView.removeView(mCallOnGoingView); mCallOnGoingView.setVisibility(View.GONE); // mCallWidget.hide(); } void doRevealAnimation() { final int h = mCloseView.getHeight() + mStatusBarView.getHeight(); if (mAnimatingReveal && mAnimating && mAnimY < h) { incrementAnim(); if (mAnimY >= h) { mAnimY = h; updateExpandedViewPos((int) mAnimY); } else { updateExpandedViewPos((int) mAnimY); mCurAnimationTime += ANIM_FRAME_DURATION; mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL), mCurAnimationTime); } } } void prepareTracking(int y, boolean opening) { mTracking = true; mVelocityTracker = VelocityTracker.obtain(); if (opening) { mAnimAccel = 2000.0f; mAnimVel = 200; mAnimY = mStatusBarView.getHeight(); updateExpandedViewPos((int) mAnimY); mAnimating = true; mAnimatingReveal = true; mHandler.removeMessages(MSG_ANIMATE); mHandler.removeMessages(MSG_ANIMATE_REVEAL); long now = SystemClock.uptimeMillis(); mAnimLastTime = now; mCurAnimationTime = now + ANIM_FRAME_DURATION; mAnimating = true; mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL), mCurAnimationTime); makeExpandedVisible(); } else { // it's open, close it? if (mAnimating) { mAnimating = false; mHandler.removeMessages(MSG_ANIMATE); } updateExpandedViewPos(y + mViewDelta); } } void performFling(int y, float vel, boolean always) { mAnimatingReveal = false; mDisplayHeight = mDisplay.getHeight(); mAnimY = y; mAnimVel = vel; // Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + // mAnimVel); if (mExpanded) { if (!always && (vel > 200.0f || (y > (mDisplayHeight - 25) && vel > -200.0f))) { // We are expanded, but they didn't move sufficiently to cause // us to retract. Animate back to the expanded position. mAnimAccel = 2000.0f; if (vel < 0) { mAnimVel = 0; } } else { // We are expanded and are now going to animate away. mAnimAccel = -2000.0f; if (vel > 0) { mAnimVel = 0; } } } else { if (always || (vel > 200.0f || (y > (mDisplayHeight / 2) && vel > -200.0f))) { // We are collapsed, and they moved enough to allow us to // expand. Animate in the notifications. mAnimAccel = 2000.0f; if (vel < 0) { mAnimVel = 0; } } else { // We are collapsed, but they didn't move sufficiently to cause // us to retract. Animate back to the collapsed position. mAnimAccel = -2000.0f; if (vel > 0) { mAnimVel = 0; } } } // Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel // + " mAnimAccel=" + mAnimAccel); long now = SystemClock.uptimeMillis(); mAnimLastTime = now; mCurAnimationTime = now + ANIM_FRAME_DURATION; mAnimating = true; mHandler.removeMessages(MSG_ANIMATE); mHandler.removeMessages(MSG_ANIMATE_REVEAL); mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime); stopTracking(); } boolean interceptTouchEvent(MotionEvent event) { if (SPEW) { Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled=" + mDisabled); } if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) { return false; } final int statusBarSize = mStatusBarView.getHeight(); final int hitSize = statusBarSize * 2; if (event.getAction() == MotionEvent.ACTION_DOWN) { final int y = (int) event.getRawY(); if (!mExpanded) { mViewDelta = statusBarSize - y; } else { mTrackingView.getLocationOnScreen(mAbsPos); mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y; } if ((!mExpanded && y < hitSize) || (mExpanded && y > (mDisplay.getHeight() - hitSize))) { // We drop events at the edge of the screen to make the // windowshade come // down by accident less, especially when pushing open a device // with a keyboard // that rotates (like g1 and droid) int x = (int) event.getRawX(); final int edgeBorder = mEdgeBorder; if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) { prepareTracking(y, !mExpanded);// opening if we're not // already fully visible mVelocityTracker.addMovement(event); } if (mIsStatusBarBrightNess) { mIsBrightNessMode = 0; if (!mIsAutoBrightNess) { mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SAMSUNG_MAGIC), ViewConfiguration.getGlobalActionKeyTimeout() * 2); } } } } else if (mTracking) { mVelocityTracker.addMovement(event); final int minY = statusBarSize + mCloseView.getHeight(); if (event.getAction() == MotionEvent.ACTION_MOVE) { int y = (int) event.getRawY(); if (mAnimatingReveal && y < minY) { // nothing if (mIsStatusBarBrightNess && mIsBrightNessMode == 1) { doBrightNess(event); } } else { if (mIsStatusBarBrightNess) { if (!mIsAutoBrightNess && mHandler.hasMessages(MSG_SAMSUNG_MAGIC)) { mHandler.removeMessages(MSG_SAMSUNG_MAGIC); } if (mIsBrightNessMode == 1) { mIsBrightNessMode = 2; } } mAnimatingReveal = false; updateExpandedViewPos(y + mViewDelta); } } else if (event.getAction() == MotionEvent.ACTION_UP) { mVelocityTracker.computeCurrentVelocity(1000); float yVel = mVelocityTracker.getYVelocity(); // boolean negative = yVel < 0; float xVel = mVelocityTracker.getXVelocity(); if (xVel < 0) { xVel = -xVel; } if (xVel > 150.0f) { xVel = 150.0f; // limit how much we care about the x axis } float vel = (float) Math.hypot(yVel, xVel); if (yVel < 0) { // negative vel = -vel; } if (mIsStatusBarBrightNess && mHandler.hasMessages(MSG_SAMSUNG_MAGIC)) { mHandler.removeMessages(MSG_SAMSUNG_MAGIC); } if (mIsBrightNessMode == 0) { performFling((int) event.getRawY(), vel, false); } } } return false; } private class Launcher implements View.OnClickListener { private PendingIntent mIntent; private String mPkg; private String mTag; private int mId; Launcher(PendingIntent intent, String pkg, String tag, int id) { mIntent = intent; mPkg = pkg; mTag = tag; mId = id; } public void onClick(View v) { try { // The intent we are sending is for the application, which // won't have permission to immediately start an activity after // the user switches to home. We know it is safe to do at this // point, so make sure new activity switches are now allowed. ActivityManagerNative.getDefault().resumeAppSwitches(); } catch (RemoteException e) { } if (mIntent != null) { int[] pos = new int[2]; v.getLocationOnScreen(pos); Intent overlay = new Intent(); overlay.setSourceBounds(new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight())); try { mIntent.send(StatusBarService.this, 0, overlay); } catch (PendingIntent.CanceledException e) { // the stack trace isn't very helpful here. Just log the // exception message. Slog.w(TAG, "Sending contentIntent failed: " + e); } } try { mBarService.onNotificationClick(mPkg, mTag, mId); } catch (RemoteException ex) { // system process is dead if we're here. } // close the shade if it was open animateCollapse(); } } private void tick(StatusBarNotification n) { // 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.notification.tickerText != null && mStatusBarView.getWindowToken() != null) { if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) { mTicker.addEntry(n); } } } /** * Cancel this notification and tell the StatusBarManagerService / * NotificationManagerService about the failure. WARNING: this will call * back into us. Don't hold any locks. */ void handleNotificationError(IBinder key, StatusBarNotification n, String message) { removeNotification(key); try { mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message); } catch (RemoteException ex) { // The end is nigh. } } private class BrightNessContentObserver extends ContentObserver { public BrightNessContentObserver() { super(new Handler()); } @Override public void onChange(boolean selfChange) { mIsAutoBrightNess = checkAutoBrightNess(); } } private class MyTicker extends Ticker { MyTicker(Context context, StatusBarView sb) { super(context, sb); } @Override void tickerStarting() { if (SPEW) Slog.d(TAG, "tickerStarting"); mTicking = true; mIcons.setVisibility(View.GONE); mTickerView.setVisibility(View.VISIBLE); mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null)); mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null)); if (mExpandedVisible) { setDateViewVisibility(false, com.android.internal.R.anim.push_up_out); } } @Override void tickerDone() { if (SPEW) Slog.d(TAG, "tickerDone"); mTicking = false; mIcons.setVisibility(View.VISIBLE); mTickerView.setVisibility(View.GONE); mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null)); mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out, null)); if (mExpandedVisible) { setDateViewVisibility(true, com.android.internal.R.anim.push_down_in); } } void tickerHalting() { if (SPEW) Slog.d(TAG, "tickerHalting"); mTicking = false; mIcons.setVisibility(View.VISIBLE); mTickerView.setVisibility(View.GONE); mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null)); mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out, null)); if (mExpandedVisible) { setDateViewVisibility(true, com.android.internal.R.anim.fade_in); } } } private Animation loadAnim(int id, Animation.AnimationListener listener) { Animation anim = AnimationUtils.loadAnimation(StatusBarService.this, id); if (listener != null) { anim.setAnimationListener(listener); } return anim; } public String viewInfo(View v) { return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom() + " " + v.getWidth() + "x" + v.getHeight() + ")"; } protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump StatusBar from from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); return; } synchronized (mQueueLock) { pw.println("Current Status Bar state:"); pw.println(" mExpanded=" + mExpanded + ", mExpandedVisible=" + mExpandedVisible); pw.println(" mTicking=" + mTicking); pw.println(" mTracking=" + mTracking); pw.println(" mAnimating=" + mAnimating + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel + ", mAnimAccel=" + mAnimAccel); pw.println(" mCurAnimationTime=" + mCurAnimationTime + " mAnimLastTime=" + mAnimLastTime); pw.println(" mDisplayHeight=" + mDisplayHeight + " mAnimatingReveal=" + mAnimatingReveal + " mViewDelta=" + mViewDelta); pw.println(" mDisplayHeight=" + mDisplayHeight); pw.println(" mExpandedParams: " + mExpandedParams); pw.println(" mExpandedView: " + viewInfo(mExpandedView)); pw.println(" mExpandedDialog: " + mExpandedDialog); pw.println(" mTrackingParams: " + mTrackingParams); pw.println(" mTrackingView: " + viewInfo(mTrackingView)); pw.println(" mOngoingTitle: " + viewInfo(mOngoingTitle)); pw.println(" mOngoingItems: " + viewInfo(mOngoingItems)); pw.println(" mLatestTitle: " + viewInfo(mLatestTitle)); pw.println(" mLatestItems: " + viewInfo(mLatestItems)); pw.println(" mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle)); pw.println(" mCloseView: " + viewInfo(mCloseView)); pw.println(" mTickerView: " + viewInfo(mTickerView)); pw.println(" mScrollView: " + viewInfo(mScrollView) + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY()); pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout)); } if (true) { // must happen on ui thread mHandler.post(new Runnable() { public void run() { Slog.d(TAG, "mStatusIcons:"); mStatusIcons.debug(); } }); } } void onBarViewAttached() { WindowManager.LayoutParams lp; int pixelFormat; Drawable bg; // / ---------- Tracking View -------------- pixelFormat = PixelFormat.TRANSLUCENT; bg = mTrackingView.getBackground(); if (bg != null) { pixelFormat = bg.getOpacity(); } lp = new WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, pixelFormat); // lp.token = mStatusBarView.getWindowToken(); lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; lp.setTitle("TrackingView"); lp.y = mTrackingPosition; mTrackingParams = lp; WindowManagerImpl.getDefault().addView(mTrackingView, lp); } void onTrackingViewAttached() { WindowManager.LayoutParams lp; int pixelFormat; Drawable bg; // / ---------- Expanded View -------------- pixelFormat = PixelFormat.TRANSLUCENT; final int disph = mDisplay.getHeight(); lp = mExpandedDialog.getWindow().getAttributes(); lp.width = ViewGroup.LayoutParams.MATCH_PARENT; lp.height = getExpandedHeight(); lp.x = 0; mTrackingPosition = lp.y = -disph; // sufficiently large negative lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_DITHER | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; lp.format = pixelFormat; lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; lp.setTitle("StatusBarExpanded"); mExpandedDialog.getWindow().setAttributes(lp); mExpandedDialog.getWindow().setFormat(pixelFormat); mExpandedParams = lp; mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); mExpandedDialog.setContentView(mExpandedView, new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); mExpandedDialog.getWindow().setBackgroundDrawable(null); mExpandedDialog.show(); FrameLayout hack = (FrameLayout) mExpandedView.getParent(); mStatusBarView.setVisibility(View.VISIBLE); } void setDateViewVisibility(boolean visible, int anim) { mDateView.setUpdates(visible); mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); mDateView.startAnimation(loadAnim(anim, null)); } 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 updateExpandedViewPos(int expandedPosition) { if (SPEW) { Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition + " mTrackingParams.y=" + ((mTrackingParams == null) ? "???" : mTrackingParams.y) + " mTrackingPosition=" + mTrackingPosition); } int h = mStatusBarView.getHeight(); int disph = mDisplay.getHeight(); // If the expanded view is not visible, make sure they're still off // screen. // Maybe the view was resized. if (!mExpandedVisible) { if (mTrackingView != null) { mTrackingPosition = -disph; if (mTrackingParams != null) { mTrackingParams.y = mTrackingPosition; WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams); } } if (mExpandedParams != null) { mExpandedParams.y = -disph; mExpandedDialog.getWindow().setAttributes(mExpandedParams); } return; } // tracking view... int pos; if (expandedPosition == EXPANDED_FULL_OPEN) { pos = h; } else if (expandedPosition == EXPANDED_LEAVE_ALONE) { pos = mTrackingPosition; } else { if (expandedPosition <= disph) { pos = expandedPosition; } else { pos = disph; } pos -= disph - h; } mTrackingPosition = mTrackingParams.y = pos; mTrackingParams.height = disph - h; WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams); if (mExpandedParams != null) { mCloseView.getLocationInWindow(mPositionTmp); final int closePos = mPositionTmp[1]; mExpandedContents.getLocationInWindow(mPositionTmp); final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight(); if (expandedPosition != EXPANDED_LEAVE_ALONE) { mExpandedParams.y = pos + mTrackingView.getHeight() - (mTrackingParams.height - closePos) - contentsBottom; int max = h; if (mExpandedParams.y > max) { mExpandedParams.y = max; } int min = mTrackingPosition; if (mExpandedParams.y < min) { mExpandedParams.y = min; } boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h; if (!visible) { // if the contents aren't visible, move the expanded view // way off screen // because the window itself extends below the content view. mExpandedParams.y = -disph; } mExpandedDialog.getWindow().setAttributes(mExpandedParams); if (SPEW) Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")"); visibilityChanged(visible); } } if (SPEW) { Slog.d(TAG, "updateExpandedViewPos after expandedPosition=" + expandedPosition + " mTrackingParams.y=" + mTrackingParams.y + " mTrackingPosition=" + mTrackingPosition + " mExpandedParams.y=" + mExpandedParams.y + " mExpandedParams.height=" + mExpandedParams.height); } } int getExpandedHeight() { return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight(); } void updateExpandedHeight() { if (mExpandedView != null) { mExpandedParams.height = getExpandedHeight(); mExpandedDialog.getWindow().setAttributes(mExpandedParams); } } /** * The LEDs are turned o)ff when the notification panel is shown, even just * a little bit. This was added last-minute and is inconsistent with the way * the rest of the notifications are handled, because the notification isn't * really cancelled. The lights are just turned off. If any other * notifications happen, the lights will turn back on. Steve says this is * what he wants. (see bug 1131461) */ void visibilityChanged(boolean visible) { if (mPanelSlightlyVisible != visible) { mPanelSlightlyVisible = visible; try { mBarService.onPanelRevealed(); } catch (RemoteException ex) { // Won't fail unless the world has ended. } } } /* * void performDisableActions(int net) { int old = mDisabled; int diff = net * ^ old; mDisabled = net; // act accordingly if ((diff & * StatusBarManager.DISABLE_EXPAND) != 0) { if ((net & * StatusBarManager.DISABLE_EXPAND) != 0) { Slog.d(TAG, * "DISABLE_EXPAND: yes"); animateCollapse(); } } if ((diff & * StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { if ((net & * StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { Slog.d(TAG, * "DISABLE_NOTIFICATION_ICONS: yes"); if (mTicking) { * mNotificationIcons.setVisibility(View.INVISIBLE); mTicker.halt(); } else * { setNotificationIconVisibility(false, * com.android.internal.R.anim.fade_out); } } else { Slog.d(TAG, * "DISABLE_NOTIFICATION_ICONS: no"); if (!mExpandedVisible) { * setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); * } } } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != * 0) { Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: " + (((net & * StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "yes" : "no")); if * (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { * mTicker.halt(); } } } */ private View.OnClickListener mClearButtonListener = new View.OnClickListener() { public void onClick(View v) { try { mBarService.onClearAllNotifications(); } catch (RemoteException ex) { // system process is dead if we're here. } animateCollapse(); } }; private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { animateCollapse(); } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { animateCollapse(); mStatusBarView.marqueeStatusBar(); } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { updateResources(); updatePropFactorValue(); } } }; /** * 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() { Resources res = getResources(); mClearButton.setText(getText(R.string.status_bar_clear_all_button)); mOngoingTitle.setText(getText(R.string.status_bar_ongoing_events_title)); mLatestTitle.setText(getText(R.string.status_bar_latest_events_title)); mNoNotificationsTitle.setText(getText(R.string.status_bar_no_notifications_title)); mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); if (false) Slog.v(TAG, "updateResources"); } // // tracing // // void postStartTracing() { // mHandler.postDelayed(mStartTracing, 3000); // } public View.OnClickListener mMusicToggleButtonListener = new View.OnClickListener() { public void onClick(View v) { mMusicControls.visibilityToggled(); } }; void vibrate() { android.os.Vibrator vib = (android.os.Vibrator) getSystemService(Context.VIBRATOR_SERVICE); vib.vibrate(250); } Runnable mStartTracing = new Runnable() { public void run() { vibrate(); SystemClock.sleep(250); Slog.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(); Slog.d(TAG, "stopTracing"); vibrate(); } }; private Float mPropFactor; private CallOnGoingView mCallOnGoingView; private boolean checkAutoBrightNess() { return Settings.System.getInt(getContentResolver(), "screen_brightness_mode", 0) == 1; } private int checkMinMax(int brightness) { if (0x1E > brightness) // brightness < 0x1E return 0x1E; else if (0xFF < brightness) { // brightness > 0xFF return 0xFF; } return brightness; } private void doBrightNess(MotionEvent e) { int screenBrightness = checkMinMax(Float.valueOf((e.getRawX() * mPropFactor.floatValue())) .intValue()); Settings.System.putInt(getContentResolver(), "screen_brightness", screenBrightness); // Log.e(TAG, "Screen brightness: " + screenBrightness); try { IPowerManager pw = IPowerManager.Stub.asInterface(ServiceManager.getService("power")); if (pw != null) { pw.setBacklightBrightness(screenBrightness); } } catch (RemoteException e1) { } } private void updatePropFactorValue() { mPropFactor = Float.valueOf((float) android.os.Power.BRIGHTNESS_ON / Integer.valueOf(mDisplay.getWidth()).floatValue()); } public void showCallOnGoingView() { // if (mStatusBarView.indexOfChild(mCallOnGoingView) == -1) { // mStatusBarView.addView(mCallOnGoingView); // } mCallOnGoingView.setVisibility(View.VISIBLE); // mCallWidget.show(); } public void updateTheme() { int oldDisabled = mDisabled; disable(1); reloadResources(); disable(oldDisabled); } void reloadResources() { // TODO Auto-generated method stub } public void updateThemeForNotificationView(Resources r, View v) { // TODO FINISH THIS View temp = v.findViewById(0x1020016); if (temp != null) { } } public void grabScreen() { } class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler) { super(handler); } void observe() { ContentResolver resolver = mContext.getContentResolver(); resolver.registerContentObserver( Settings.System.getUriFor("tweaks_use_custom_music_controls"), false, this); } @Override public void onChange(boolean selfChange) { updateSettings(); } } public void updateSettings() { useCustomMusic = Settings.System.getInt(getContentResolver(), "tweaks_use_custom_music_controls", 1) == 1; swipeToClearNotifications = Settings.System.getInt(getContentResolver(), "tweaks_swipe_to_clear_notifications", 1) == 1; longPressToClearNotifications = Settings.System.getInt(getContentResolver(), "tweaks_long_press_to_clear_notifications", 0) == 1; useAlternativeToggleLayout = Settings.System.getInt(getContentResolver(), "tweaks_use_alternative_toggle_layout", 0) == 1; if (!useCustomMusic) { mMusicToggleButton.setVisibility(View.GONE); mMusicControls.disable(); } else { mMusicControls.setupControls(); } } }