BaseStatusBar.java revision 26cda27d2e6f1341514943b39a4da1e55ba2197e
10f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang/* 20f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * Copyright (C) 2010 The Android Open Source Project 30f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * 40f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * Licensed under the Apache License, Version 2.0 (the "License"); 50f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * you may not use this file except in compliance with the License. 60f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * You may obtain a copy of the License at 70f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * 80f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * http://www.apache.org/licenses/LICENSE-2.0 90f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * 100f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * Unless required by applicable law or agreed to in writing, software 110f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * distributed under the License is distributed on an "AS IS" BASIS, 120f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * See the License for the specific language governing permissions and 140f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * limitations under the License. 150f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang */ 160f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 170f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangpackage com.android.systemui.statusbar; 180f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 1961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport java.util.ArrayList; 200f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 210f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.app.ActivityManagerNative; 220f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.app.KeyguardManager; 230f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.app.Notification; 240f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.app.PendingIntent; 250f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.content.Context; 260f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.content.Intent; 270f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.content.pm.ApplicationInfo; 280f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.content.pm.PackageManager.NameNotFoundException; 290f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.database.ContentObserver; 300f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.graphics.Rect; 310f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.net.Uri; 320f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.os.Build; 330f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.os.Handler; 340f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.os.IBinder; 35210d73df0b77b4c8be67ecc92afb238dc8c7ccfaJungshik Jangimport android.os.Message; 36b509c2ecd99619248b7a07fb0fa978bb27f25cc3Jungshik Jangimport android.os.RemoteException; 370f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.os.ServiceManager; 380f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.provider.Settings; 390f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.text.TextUtils; 400f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.util.Log; 4107600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kimimport android.util.Slog; 420f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.view.Display; 430f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.view.IWindowManager; 440f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.view.LayoutInflater; 450f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.view.MenuItem; 460f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.view.MotionEvent; 47c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kimimport android.view.View; 48c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kimimport android.view.ViewGroup; 490f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.view.ViewGroup.LayoutParams; 500f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.view.WindowManager; 510f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport android.view.WindowManagerImpl; 5207600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kimimport android.widget.ImageView; 5307600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kimimport android.widget.LinearLayout; 5407600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kimimport android.widget.RemoteViews; 5507600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kimimport android.widget.PopupMenu; 5607600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim 5707600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kimimport com.android.internal.statusbar.IStatusBarService; 580f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport com.android.internal.statusbar.StatusBarIcon; 590f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport com.android.internal.statusbar.StatusBarIconList; 600f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport com.android.internal.statusbar.StatusBarNotification; 6179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport com.android.internal.widget.SizeAdaptiveLayout; 620f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport com.android.systemui.SearchPanelView; 6379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport com.android.systemui.SystemUI; 6479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport com.android.systemui.recent.RecentsPanelView; 650f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport com.android.systemui.recent.RecentTasksLoader; 660f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport com.android.systemui.recent.TaskDescription; 670f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport com.android.systemui.statusbar.CommandQueue; 680f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport com.android.systemui.statusbar.policy.NotificationRowLayout; 690f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport com.android.systemui.statusbar.tablet.StatusBarPanel; 700f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 710f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangimport com.android.systemui.R; 720f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 730f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jangpublic abstract class BaseStatusBar extends SystemUI implements 740f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang CommandQueue.Callbacks, RecentsPanelView.OnRecentsPanelVisibilityChangedListener { 750f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang static final String TAG = "StatusBar"; 760f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang private static final boolean DEBUG = false; 770f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 780f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected static final int MSG_OPEN_RECENTS_PANEL = 1020; 790f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected static final int MSG_CLOSE_RECENTS_PANEL = 1021; 800f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected static final int MSG_PRELOAD_RECENT_APPS = 1022; 810f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023; 820f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected static final int MSG_OPEN_SEARCH_PANEL = 1024; 830f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected static final int MSG_CLOSE_SEARCH_PANEL = 1025; 840f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected static final int MSG_SHOW_INTRUDER = 1026; 850f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected static final int MSG_HIDE_INTRUDER = 1027; 860f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 870f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected static final boolean ENABLE_INTRUDERS = false; 880f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 890f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang // Should match the value in PhoneWindowManager 900f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; 910f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 920f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang public static final int EXPANDED_LEAVE_ALONE = -10000; 930f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang public static final int EXPANDED_FULL_OPEN = -10001; 940f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 950f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected CommandQueue mCommandQueue; 960f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected IStatusBarService mBarService; 970f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected H mHandler = createHandler(); 9860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang 9960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang // all notifications 10060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang protected NotificationData mNotificationData = new NotificationData(); 10160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang protected NotificationRowLayout mPile; 10260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang 10360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang protected StatusBarNotification mCurrentlyIntrudingNotification; 10460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang 10560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang // used to notify status bar for suppressing notification LED 10660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang protected boolean mPanelSlightlyVisible; 10760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang 10860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang // Search panel 10960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang protected SearchPanelView mSearchPanelView; 11060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang 11160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang // Recent apps 1120f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected RecentsPanelView mRecentsPanel; 1130f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected RecentTasksLoader mRecentTasksLoader; 1140f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 1150f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected PopupMenu mNotificationBlamePopup; 1160f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 1170f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang // UI-specific methods 118377dcbd53af4529c352d453424539b069909fce4Jungshik Jang 1190f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang /** 1200f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * Create all windows necessary for the status bar (including navigation, overlay panels, etc) 1210f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang * and add them to the window manager. 1220f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang */ 1230f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected abstract void createAndAddWindows(); 1240f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 1250f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang protected Display mDisplay; 1260f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang private IWindowManager mWindowManager; 1270f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang private boolean mDeviceProvisioned = false; 1280f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 12979c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang public IWindowManager getWindowManager() { 1300f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang return mWindowManager; 1310f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 1320f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 1330f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang public Display getDisplay() { 134c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim return mDisplay; 1355fba96df30b6b50b3cb9fe1d783320b1cc3bd6eaJinsuk Kim } 1360f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 1370f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang public IStatusBarService getStatusBarService() { 1380f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang return mBarService; 1390f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 1400f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 14179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang protected boolean isDeviceProvisioned() { 1420f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang return mDeviceProvisioned; 1430f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 144a466929979a92a578d4ba00093fefa57cfb982b4Jungshik Jang 1450f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang private ContentObserver mProvisioningObserver = new ContentObserver(new Handler()) { 146c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim @Override 1475fba96df30b6b50b3cb9fe1d783320b1cc3bd6eaJinsuk Kim public void onChange(boolean selfChange) { 1480f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang final boolean provisioned = 0 != Settings.Secure.getInt( 1490f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang mContext.getContentResolver(), Settings.Secure.DEVICE_PROVISIONED, 0); 1500f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang if (provisioned != mDeviceProvisioned) { 15179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang mDeviceProvisioned = provisioned; 1520f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang updateNotificationIcons(); 1530f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 1540f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 1550f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang }; 1560f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 1570f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang public void start() { 15807600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)) 15907600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim .getDefaultDisplay(); 16007600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim 16107600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim mProvisioningObserver.onChange(false); // set up 16207600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim mContext.getContentResolver().registerContentObserver( 16307600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim Settings.Secure.getUriFor(Settings.Secure.DEVICE_PROVISIONED), true, 16407600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim mProvisioningObserver); 1650f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 1660f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang mWindowManager = IWindowManager.Stub.asInterface( 1670f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang ServiceManager.getService(Context.WINDOW_SERVICE)); 1680f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 16907600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim mBarService = IStatusBarService.Stub.asInterface( 17007600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim ServiceManager.getService(Context.STATUS_BAR_SERVICE)); 17107600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim 17207600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim // Connect in to the status bar manager service 17307600116b59e2accd1e95b9029c2c9819cd76c5aJinsuk Kim StatusBarIconList iconList = new StatusBarIconList(); 1740f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>(); 1750f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>(); 1760f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang mCommandQueue = new CommandQueue(this, iconList); 1770f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 1780f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang int[] switches = new int[7]; 1790f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang ArrayList<IBinder> binders = new ArrayList<IBinder>(); 1800f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang try { 1810f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications, 1820f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang switches, binders); 18361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang } catch (RemoteException ex) { 1840f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang // If the system process isn't there we're doomed anyway. 18561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang } 1860f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 18761f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang createAndAddWindows(); 1880f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 1890f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang disable(switches[0]); 1900f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang setSystemUiVisibility(switches[1], 0xffffffff); 1910f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang topAppWindowChanged(switches[2] != 0); 1920f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang // StatusBarManagerService has a back up of IME token and it's restored here. 1930f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang setImeWindowStatus(binders.get(0), switches[3], switches[4]); 1940f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang setHardKeyboardStatus(switches[5] != 0, switches[6] != 0); 1950f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 1960f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang // Set up the initial icon state 1970f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang int N = iconList.size(); 1980f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang int viewIndex = 0; 1990f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang for (int i=0; i<N; i++) { 2000f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang StatusBarIcon icon = iconList.getIcon(i); 2010f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang if (icon != null) { 2020f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang addIcon(iconList.getSlot(i), i, viewIndex, icon); 2030f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang viewIndex++; 2040f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 2050f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 2060f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 2070f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang // Set up the initial notification state 2080f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang N = notificationKeys.size(); 2090f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang if (N == notifications.size()) { 2100f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang for (int i=0; i<N; i++) { 2110f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang addNotification(notificationKeys.get(i), notifications.get(i)); 2120f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 2130f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } else { 21426dc71e7feefb2417fd5f007af68614c1c197cf8Jungshik Jang Log.wtf(TAG, "Notification list length mismatch: keys=" + N 21579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang + " notifications=" + notifications.size()); 21679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } 2170f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang 2180f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang if (DEBUG) { 2190f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang Slog.d(TAG, String.format( 22060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x", 22160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang iconList.size(), 22260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang switches[0], 22360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang switches[1], 22460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang switches[2], 22579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang switches[3] 22660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang )); 22760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang } 22860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang } 2298960d1b1552729e3dfd33deee951ac75933ad8e5Jinsuk Kim 23026dc71e7feefb2417fd5f007af68614c1c197cf8Jungshik Jang protected View updateNotificationVetoButton(View row, StatusBarNotification n) { 23126dc71e7feefb2417fd5f007af68614c1c197cf8Jungshik Jang View vetoButton = row.findViewById(R.id.veto); 23226dc71e7feefb2417fd5f007af68614c1c197cf8Jungshik Jang if (n.isClearable()) { 23360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang final String _pkg = n.pkg; 23460cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang final String _tag = n.tag; 23560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang final int _id = n.id; 23679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang vetoButton.setOnClickListener(new View.OnClickListener() { 23760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang public void onClick(View v) { 23860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang try { 23960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang mBarService.onNotificationClear(_pkg, _tag, _id); 24060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang } catch (RemoteException ex) { 241c516d65fd96cdc39f9935ddb80d26ee6499a77bfYuncheol Heo // system process is dead if we're here. 24260cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang } 24360cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang } 24479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang }); 24560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang vetoButton.setVisibility(View.VISIBLE); 24660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang } else { 24760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang vetoButton.setVisibility(View.GONE); 24860cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang } 2493dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang return vetoButton; 2503dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang } 2513dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang 2523dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang 2533dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang protected void applyLegacyRowBackground(StatusBarNotification sbn, View content) { 2543dcdd71e203f7751155ec83468e0f8092e8ea80bJungshik Jang if (sbn.notification.contentView.getLayoutId() != 25560cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang com.android.internal.R.layout.notification_template_base) { 25660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang int version = 0; 25760cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang try { 25861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.pkg, 0); 25960cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang version = info.targetSdkVersion; 26060cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang } catch (NameNotFoundException ex) { 26160cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang Slog.e(TAG, "Failed looking up ApplicationInfo for " + sbn.pkg, ex); 2627ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim } 2637ecfbaed6e902aea151bc1919cf7771bbd868fc4Jinsuk Kim if (version > 0 && version < Build.VERSION_CODES.GINGERBREAD) { 264a13da0d5913757e2456020c69481f98d0e44c090Jungshik Jang content.setBackgroundResource(R.drawable.notification_row_legacy_bg); 26579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang } else { 26660cffce420db4c3395f86d3b9bb36003adf26f5dJungshik Jang content.setBackgroundResource(com.android.internal.R.drawable.notification_bg); 2670f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 2680f8b4b770c49b83fa8260833d8e1ec5c721a05d3Jungshik Jang } 269 } 270 271 private void startApplicationDetailsActivity(String packageName) { 272 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, 273 Uri.fromParts("package", packageName, null)); 274 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 275 mContext.startActivity(intent); 276 } 277 278 protected View.OnLongClickListener getNotificationLongClicker() { 279 return new View.OnLongClickListener() { 280 @Override 281 public boolean onLongClick(View v) { 282 final String packageNameF = (String) v.getTag(); 283 if (packageNameF == null) return false; 284 if (v.getWindowToken() == null) return false; 285 mNotificationBlamePopup = new PopupMenu(mContext, v); 286 mNotificationBlamePopup.getMenuInflater().inflate( 287 R.menu.notification_popup_menu, 288 mNotificationBlamePopup.getMenu()); 289 mNotificationBlamePopup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { 290 public boolean onMenuItemClick(MenuItem item) { 291 if (item.getItemId() == R.id.notification_inspect_item) { 292 startApplicationDetailsActivity(packageNameF); 293 animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE); 294 } else { 295 return false; 296 } 297 return true; 298 } 299 }); 300 mNotificationBlamePopup.show(); 301 302 return true; 303 } 304 }; 305 } 306 307 public void dismissPopups() { 308 if (mNotificationBlamePopup != null) { 309 mNotificationBlamePopup.dismiss(); 310 mNotificationBlamePopup = null; 311 } 312 } 313 314 public void dismissIntruder() { 315 // pass 316 } 317 318 @Override 319 public void toggleRecentApps() { 320 int msg = (mRecentsPanel.getVisibility() == View.VISIBLE) 321 ? MSG_CLOSE_RECENTS_PANEL : MSG_OPEN_RECENTS_PANEL; 322 mHandler.removeMessages(msg); 323 mHandler.sendEmptyMessage(msg); 324 } 325 326 @Override 327 public void preloadRecentApps() { 328 int msg = MSG_PRELOAD_RECENT_APPS; 329 mHandler.removeMessages(msg); 330 mHandler.sendEmptyMessage(msg); 331 } 332 333 @Override 334 public void cancelPreloadRecentApps() { 335 int msg = MSG_CANCEL_PRELOAD_RECENT_APPS; 336 mHandler.removeMessages(msg); 337 mHandler.sendEmptyMessage(msg); 338 } 339 340 @Override 341 public void showSearchPanel() { 342 int msg = MSG_OPEN_SEARCH_PANEL; 343 mHandler.removeMessages(msg); 344 mHandler.sendEmptyMessage(msg); 345 } 346 347 @Override 348 public void hideSearchPanel() { 349 int msg = MSG_CLOSE_SEARCH_PANEL; 350 mHandler.removeMessages(msg); 351 mHandler.sendEmptyMessage(msg); 352 } 353 354 @Override 355 public void onRecentsPanelVisibilityChanged(boolean visible) { 356 } 357 358 protected abstract WindowManager.LayoutParams getRecentsLayoutParams( 359 LayoutParams layoutParams); 360 361 protected abstract WindowManager.LayoutParams getSearchLayoutParams( 362 LayoutParams layoutParams); 363 364 protected void updateRecentsPanel(int recentsResId) { 365 // Recents Panel 366 boolean visible = false; 367 ArrayList<TaskDescription> recentTasksList = null; 368 boolean firstScreenful = false; 369 if (mRecentsPanel != null) { 370 visible = mRecentsPanel.isShowing(); 371 WindowManagerImpl.getDefault().removeView(mRecentsPanel); 372 if (visible) { 373 recentTasksList = mRecentsPanel.getRecentTasksList(); 374 firstScreenful = mRecentsPanel.getFirstScreenful(); 375 } 376 } 377 378 // Provide RecentsPanelView with a temporary parent to allow layout params to work. 379 LinearLayout tmpRoot = new LinearLayout(mContext); 380 mRecentsPanel = (RecentsPanelView) LayoutInflater.from(mContext).inflate( 381 recentsResId, tmpRoot, false); 382 mRecentsPanel.setRecentTasksLoader(mRecentTasksLoader); 383 mRecentTasksLoader.setRecentsPanel(mRecentsPanel); 384 mRecentsPanel.setOnTouchListener( 385 new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL, mRecentsPanel)); 386 mRecentsPanel.setVisibility(View.GONE); 387 388 389 WindowManager.LayoutParams lp = getRecentsLayoutParams(mRecentsPanel.getLayoutParams()); 390 391 WindowManagerImpl.getDefault().addView(mRecentsPanel, lp); 392 mRecentsPanel.setBar(this); 393 if (visible) { 394 mRecentsPanel.show(true, false, recentTasksList, firstScreenful); 395 } 396 397 } 398 399 protected void updateSearchPanel() { 400 // Search Panel 401 boolean visible = false; 402 if (mSearchPanelView != null) { 403 visible = mSearchPanelView.isShowing(); 404 WindowManagerImpl.getDefault().removeView(mSearchPanelView); 405 } 406 407 // Provide SearchPanel with a temporary parent to allow layout params to work. 408 LinearLayout tmpRoot = new LinearLayout(mContext); 409 mSearchPanelView = (SearchPanelView) LayoutInflater.from(mContext).inflate( 410 R.layout.status_bar_search_panel, tmpRoot, false); 411 mSearchPanelView.setOnTouchListener( 412 new TouchOutsideListener(MSG_CLOSE_SEARCH_PANEL, mSearchPanelView)); 413 mSearchPanelView.setVisibility(View.GONE); 414 415 WindowManager.LayoutParams lp = getSearchLayoutParams(mSearchPanelView.getLayoutParams()); 416 417 WindowManagerImpl.getDefault().addView(mSearchPanelView, lp); 418 mSearchPanelView.setBar(this); 419 if (visible) { 420 mSearchPanelView.show(true, false); 421 } 422 } 423 424 protected H createHandler() { 425 return new H(); 426 } 427 428 static void sendCloseSystemWindows(Context context, String reason) { 429 if (ActivityManagerNative.isSystemReady()) { 430 try { 431 ActivityManagerNative.getDefault().closeSystemDialogs(reason); 432 } catch (RemoteException e) { 433 } 434 } 435 } 436 437 protected class H extends Handler { 438 public void handleMessage(Message m) { 439 switch (m.what) { 440 case MSG_OPEN_RECENTS_PANEL: 441 if (DEBUG) Slog.d(TAG, "opening recents panel"); 442 if (mRecentsPanel != null) { 443 mRecentsPanel.show(true, true); 444 } 445 break; 446 case MSG_CLOSE_RECENTS_PANEL: 447 if (DEBUG) Slog.d(TAG, "closing recents panel"); 448 if (mRecentsPanel != null && mRecentsPanel.isShowing()) { 449 mRecentsPanel.show(false, true); 450 } 451 break; 452 case MSG_PRELOAD_RECENT_APPS: 453 if (DEBUG) Slog.d(TAG, "preloading recents"); 454 mRecentsPanel.preloadRecentTasksList(); 455 break; 456 case MSG_CANCEL_PRELOAD_RECENT_APPS: 457 if (DEBUG) Slog.d(TAG, "cancel preloading recents"); 458 mRecentsPanel.clearRecentTasksList(); 459 break; 460 case MSG_OPEN_SEARCH_PANEL: 461 if (DEBUG) Slog.d(TAG, "opening search panel"); 462 if (mSearchPanelView != null && mSearchPanelView.isAssistantAvailable()) { 463 mSearchPanelView.show(true, true); 464 } 465 break; 466 case MSG_CLOSE_SEARCH_PANEL: 467 if (DEBUG) Slog.d(TAG, "closing search panel"); 468 if (mSearchPanelView != null && mSearchPanelView.isShowing()) { 469 mSearchPanelView.show(false, true); 470 } 471 break; 472 } 473 } 474 } 475 476 public class TouchOutsideListener implements View.OnTouchListener { 477 private int mMsg; 478 private StatusBarPanel mPanel; 479 480 public TouchOutsideListener(int msg, StatusBarPanel panel) { 481 mMsg = msg; 482 mPanel = panel; 483 } 484 485 public boolean onTouch(View v, MotionEvent ev) { 486 final int action = ev.getAction(); 487 if (action == MotionEvent.ACTION_OUTSIDE 488 || (action == MotionEvent.ACTION_DOWN 489 && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) { 490 mHandler.removeMessages(mMsg); 491 mHandler.sendEmptyMessage(mMsg); 492 return true; 493 } 494 return false; 495 } 496 } 497 498 protected void workAroundBadLayerDrawableOpacity(View v) { 499 } 500 501 protected boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) { 502 int rowHeight = 503 mContext.getResources().getDimensionPixelSize(R.dimen.notification_row_min_height); 504 int minHeight = 505 mContext.getResources().getDimensionPixelSize(R.dimen.notification_min_height); 506 int maxHeight = 507 mContext.getResources().getDimensionPixelSize(R.dimen.notification_max_height); 508 StatusBarNotification sbn = entry.notification; 509 RemoteViews oneU = sbn.notification.contentView; 510 RemoteViews large = sbn.notification.bigContentView; 511 if (oneU == null) { 512 return false; 513 } 514 515 // create the row view 516 LayoutInflater inflater = (LayoutInflater)mContext.getSystemService( 517 Context.LAYOUT_INFLATER_SERVICE); 518 View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false); 519 520 // for blaming (see SwipeHelper.setLongPressListener) 521 row.setTag(sbn.pkg); 522 523 workAroundBadLayerDrawableOpacity(row); 524 View vetoButton = updateNotificationVetoButton(row, sbn); 525 vetoButton.setContentDescription(mContext.getString( 526 R.string.accessibility_remove_notification)); 527 528 // NB: the large icon is now handled entirely by the template 529 530 // bind the click event to the content area 531 ViewGroup content = (ViewGroup)row.findViewById(R.id.content); 532 ViewGroup adaptive = (ViewGroup)row.findViewById(R.id.adaptive); 533 534 content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); 535 536 PendingIntent contentIntent = sbn.notification.contentIntent; 537 if (contentIntent != null) { 538 final View.OnClickListener listener = new NotificationClicker(contentIntent, 539 sbn.pkg, sbn.tag, sbn.id); 540 content.setOnClickListener(listener); 541 } else { 542 content.setOnClickListener(null); 543 } 544 545 // TODO(cwren) normalize variable names with those in updateNotification 546 View expandedOneU = null; 547 View expandedLarge = null; 548 Exception exception = null; 549 try { 550 expandedOneU = oneU.apply(mContext, adaptive); 551 if (large != null) { 552 expandedLarge = large.apply(mContext, adaptive); 553 } 554 } 555 catch (RuntimeException e) { 556 final String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id); 557 Slog.e(TAG, "couldn't inflate view for notification " + ident, e); 558 return false; 559 } 560 561 if (expandedOneU != null) { 562 SizeAdaptiveLayout.LayoutParams params = 563 new SizeAdaptiveLayout.LayoutParams(expandedOneU.getLayoutParams()); 564 params.minHeight = rowHeight; 565 params.maxHeight = rowHeight; 566 adaptive.addView(expandedOneU, params); 567 } 568 if (expandedLarge != null) { 569 SizeAdaptiveLayout.LayoutParams params = 570 new SizeAdaptiveLayout.LayoutParams(expandedLarge.getLayoutParams()); 571 params.minHeight = rowHeight+1; 572 params.maxHeight = maxHeight; 573 adaptive.addView(expandedLarge, params); 574 } 575 row.setDrawingCacheEnabled(true); 576 577 applyLegacyRowBackground(sbn, content); 578 579 row.setTag(R.id.expandable_tag, Boolean.valueOf(large != null)); 580 entry.row = row; 581 entry.content = content; 582 entry.expanded = expandedOneU; 583 entry.setLargeView(expandedLarge); 584 585 return true; 586 } 587 588 public NotificationClicker makeClicker(PendingIntent intent, String pkg, String tag, int id) { 589 return new NotificationClicker(intent, pkg, tag, id); 590 } 591 592 private class NotificationClicker implements View.OnClickListener { 593 private PendingIntent mIntent; 594 private String mPkg; 595 private String mTag; 596 private int mId; 597 598 NotificationClicker(PendingIntent intent, String pkg, String tag, int id) { 599 mIntent = intent; 600 mPkg = pkg; 601 mTag = tag; 602 mId = id; 603 } 604 605 public void onClick(View v) { 606 try { 607 // The intent we are sending is for the application, which 608 // won't have permission to immediately start an activity after 609 // the user switches to home. We know it is safe to do at this 610 // point, so make sure new activity switches are now allowed. 611 ActivityManagerNative.getDefault().resumeAppSwitches(); 612 // Also, notifications can be launched from the lock screen, 613 // so dismiss the lock screen when the activity starts. 614 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); 615 } catch (RemoteException e) { 616 } 617 618 if (mIntent != null) { 619 int[] pos = new int[2]; 620 v.getLocationOnScreen(pos); 621 Intent overlay = new Intent(); 622 overlay.setSourceBounds( 623 new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight())); 624 try { 625 mIntent.send(mContext, 0, overlay); 626 } catch (PendingIntent.CanceledException e) { 627 // the stack trace isn't very helpful here. Just log the exception message. 628 Slog.w(TAG, "Sending contentIntent failed: " + e); 629 } 630 631 KeyguardManager kgm = 632 (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); 633 if (kgm != null) kgm.exitKeyguardSecurely(null); 634 } 635 636 try { 637 mBarService.onNotificationClick(mPkg, mTag, mId); 638 } catch (RemoteException ex) { 639 // system process is dead if we're here. 640 } 641 642 // close the shade if it was open 643 animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE); 644 visibilityChanged(false); 645 646 // If this click was on the intruder alert, hide that instead 647// mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER); 648 } 649 } 650 /** 651 * The LEDs are turned o)ff when the notification panel is shown, even just a little bit. 652 * This was added last-minute and is inconsistent with the way the rest of the notifications 653 * are handled, because the notification isn't really cancelled. The lights are just 654 * turned off. If any other notifications happen, the lights will turn back on. Steve says 655 * this is what he wants. (see bug 1131461) 656 */ 657 protected void visibilityChanged(boolean visible) { 658 if (mPanelSlightlyVisible != visible) { 659 mPanelSlightlyVisible = visible; 660 try { 661 mBarService.onPanelRevealed(); 662 } catch (RemoteException ex) { 663 // Won't fail unless the world has ended. 664 } 665 } 666 } 667 668 /** 669 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService 670 * about the failure. 671 * 672 * WARNING: this will call back into us. Don't hold any locks. 673 */ 674 void handleNotificationError(IBinder key, StatusBarNotification n, String message) { 675 removeNotification(key); 676 try { 677 mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message); 678 } catch (RemoteException ex) { 679 // The end is nigh. 680 } 681 } 682 683 protected StatusBarNotification removeNotificationViews(IBinder key) { 684 NotificationData.Entry entry = mNotificationData.remove(key); 685 if (entry == null) { 686 Slog.w(TAG, "removeNotification for unknown key: " + key); 687 return null; 688 } 689 // Remove the expanded view. 690 ViewGroup rowParent = (ViewGroup)entry.row.getParent(); 691 if (rowParent != null) rowParent.removeView(entry.row); 692 updateExpansionStates(); 693 updateNotificationIcons(); 694 695 return entry.notification; 696 } 697 698 protected StatusBarIconView addNotificationViews(IBinder key, 699 StatusBarNotification notification) { 700 if (DEBUG) { 701 Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification); 702 } 703 // Construct the icon. 704 final StatusBarIconView iconView = new StatusBarIconView(mContext, 705 notification.pkg + "/0x" + Integer.toHexString(notification.id), 706 notification.notification); 707 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 708 709 final StatusBarIcon ic = new StatusBarIcon(notification.pkg, 710 notification.notification.icon, 711 notification.notification.iconLevel, 712 notification.notification.number, 713 notification.notification.tickerText); 714 if (!iconView.set(ic)) { 715 handleNotificationError(key, notification, "Couldn't create icon: " + ic); 716 return null; 717 } 718 // Construct the expanded view. 719 NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView); 720 if (!inflateViews(entry, mPile)) { 721 handleNotificationError(key, notification, "Couldn't expand RemoteViews for: " 722 + notification); 723 return null; 724 } 725 726 // Add the expanded view and icon. 727 int pos = mNotificationData.add(entry); 728 if (DEBUG) { 729 Slog.d(TAG, "addNotificationViews: added at " + pos); 730 } 731 updateExpansionStates(); 732 updateNotificationIcons(); 733 734 return iconView; 735 } 736 737 protected boolean expandView(NotificationData.Entry entry, boolean expand) { 738 int rowHeight = 739 mContext.getResources().getDimensionPixelSize(R.dimen.notification_row_min_height); 740 ViewGroup.LayoutParams lp = entry.row.getLayoutParams(); 741 if (entry.expandable() && expand) { 742 if (DEBUG) Slog.d(TAG, "setting expanded row height to WRAP_CONTENT"); 743 lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; 744 } else { 745 if (DEBUG) Slog.d(TAG, "setting collapsed row height to " + rowHeight); 746 lp.height = rowHeight; 747 } 748 entry.row.setLayoutParams(lp); 749 return expand; 750 } 751 752 protected void updateExpansionStates() { 753 int N = mNotificationData.size(); 754 for (int i = 0; i < N; i++) { 755 NotificationData.Entry entry = mNotificationData.get(i); 756 if (i == (N-1)) { 757 if (DEBUG) Slog.d(TAG, "expanding top notification at " + i); 758 expandView(entry, true); 759 } else { 760 if (!entry.userExpanded()) { 761 if (DEBUG) Slog.d(TAG, "collapsing notification at " + i); 762 expandView(entry, false); 763 } else { 764 if (DEBUG) Slog.d(TAG, "ignoring user-modified notification at " + i); 765 } 766 } 767 } 768 } 769 770 protected abstract void haltTicker(); 771 protected abstract void setAreThereNotifications(); 772 protected abstract void updateNotificationIcons(); 773 protected abstract void tick(IBinder key, StatusBarNotification n, boolean firstTime); 774 protected abstract void updateExpandedViewPos(int expandedPosition); 775 protected abstract int getExpandedViewMaxHeight(); 776 protected abstract boolean shouldDisableNavbarGestures(); 777 778 protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) { 779 return parent != null && parent.indexOfChild(entry.row) == 0; 780 } 781 782 public void updateNotification(IBinder key, StatusBarNotification notification) { 783 if (DEBUG) Slog.d(TAG, "updateNotification(" + key + " -> " + notification + ")"); 784 785 final NotificationData.Entry oldEntry = mNotificationData.findByKey(key); 786 if (oldEntry == null) { 787 Slog.w(TAG, "updateNotification for unknown key: " + key); 788 return; 789 } 790 791 final StatusBarNotification oldNotification = oldEntry.notification; 792 793 // XXX: modify when we do something more intelligent with the two content views 794 final RemoteViews oldContentView = oldNotification.notification.contentView; 795 final RemoteViews contentView = notification.notification.contentView; 796 final RemoteViews oldBigContentView = oldNotification.notification.bigContentView; 797 final RemoteViews bigContentView = notification.notification.bigContentView; 798 799 if (DEBUG) { 800 Slog.d(TAG, "old notification: when=" + oldNotification.notification.when 801 + " ongoing=" + oldNotification.isOngoing() 802 + " expanded=" + oldEntry.expanded 803 + " contentView=" + oldContentView 804 + " bigContentView=" + oldBigContentView 805 + " rowParent=" + oldEntry.row.getParent()); 806 Slog.d(TAG, "new notification: when=" + notification.notification.when 807 + " ongoing=" + oldNotification.isOngoing() 808 + " contentView=" + contentView 809 + " bigContentView=" + bigContentView); 810 } 811 812 // Can we just reapply the RemoteViews in place? If when didn't change, the order 813 // didn't change. 814 815 // 1U is never null 816 boolean contentsUnchanged = oldEntry.expanded != null 817 && contentView.getPackage() != null 818 && oldContentView.getPackage() != null 819 && oldContentView.getPackage().equals(contentView.getPackage()) 820 && oldContentView.getLayoutId() == contentView.getLayoutId(); 821 // large view may be null 822 boolean bigContentsUnchanged = 823 (oldEntry.getLargeView() == null && bigContentView == null) 824 || ((oldEntry.getLargeView() != null && bigContentView != null) 825 && bigContentView.getPackage() != null 826 && oldBigContentView.getPackage() != null 827 && oldBigContentView.getPackage().equals(bigContentView.getPackage()) 828 && oldBigContentView.getLayoutId() == bigContentView.getLayoutId()); 829 ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent(); 830 boolean orderUnchanged = notification.notification.when==oldNotification.notification.when 831 && notification.score == oldNotification.score; 832 // score now encompasses/supersedes isOngoing() 833 834 boolean updateTicker = notification.notification.tickerText != null 835 && !TextUtils.equals(notification.notification.tickerText, 836 oldEntry.notification.notification.tickerText); 837 boolean isTopAnyway = isTopNotification(rowParent, oldEntry); 838 if (contentsUnchanged && bigContentsUnchanged && (orderUnchanged || isTopAnyway)) { 839 if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key); 840 oldEntry.notification = notification; 841 try { 842 // Reapply the RemoteViews 843 contentView.reapply(mContext, oldEntry.expanded); 844 if (bigContentView != null && oldEntry.getLargeView() != null) { 845 bigContentView.reapply(mContext, oldEntry.getLargeView()); 846 } 847 // update the contentIntent 848 final PendingIntent contentIntent = notification.notification.contentIntent; 849 if (contentIntent != null) { 850 final View.OnClickListener listener = makeClicker(contentIntent, 851 notification.pkg, notification.tag, notification.id); 852 oldEntry.content.setOnClickListener(listener); 853 } else { 854 oldEntry.content.setOnClickListener(null); 855 } 856 // Update the icon. 857 final StatusBarIcon ic = new StatusBarIcon(notification.pkg, 858 notification.notification.icon, notification.notification.iconLevel, 859 notification.notification.number, 860 notification.notification.tickerText); 861 if (!oldEntry.icon.set(ic)) { 862 handleNotificationError(key, notification, "Couldn't update icon: " + ic); 863 return; 864 } 865 updateExpansionStates(); 866 } 867 catch (RuntimeException e) { 868 // It failed to add cleanly. Log, and remove the view from the panel. 869 Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e); 870 removeNotificationViews(key); 871 addNotificationViews(key, notification); 872 } 873 } else { 874 if (DEBUG) Slog.d(TAG, "not reusing notification for key: " + key); 875 if (DEBUG) Slog.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed")); 876 if (DEBUG) Slog.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed")); 877 if (DEBUG) Slog.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top")); 878 removeNotificationViews(key); 879 addNotificationViews(key, notification); 880 } 881 882 // Update the veto button accordingly (and as a result, whether this row is 883 // swipe-dismissable) 884 updateNotificationVetoButton(oldEntry.row, notification); 885 886 // Restart the ticker if it's still running 887 if (updateTicker) { 888 haltTicker(); 889 tick(key, notification, false); 890 } 891 892 // Recalculate the position of the sliding windows and the titles. 893 setAreThereNotifications(); 894 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 895 896 // See if we need to update the intruder. 897 if (ENABLE_INTRUDERS && oldNotification == mCurrentlyIntrudingNotification) { 898 if (DEBUG) Slog.d(TAG, "updating the current intruder:" + notification); 899 // XXX: this is a hack for Alarms. The real implementation will need to *update* 900 // the intruder. 901 if (notification.notification.fullScreenIntent == null) { // TODO(dsandler): consistent logic with add() 902 if (DEBUG) Slog.d(TAG, "no longer intrudes!"); 903 mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER); 904 } 905 } 906 } 907} 908