NotificationStation.java revision 95860491fe19b84c853fa7640be6d3c02dff2975
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.settings; 18 19import android.app.Activity; 20import android.app.ActivityManager; 21import android.app.INotificationListener; 22import android.app.INotificationManager; 23import android.app.Notification; 24import android.content.BroadcastReceiver; 25import android.content.Context; 26import android.content.Intent; 27import android.content.IntentFilter; 28import android.content.pm.ApplicationInfo; 29import android.content.pm.PackageManager; 30import android.content.res.Resources; 31import android.graphics.drawable.Drawable; 32import android.net.Uri; 33import android.os.Bundle; 34import android.os.Handler; 35import android.os.RemoteException; 36import android.os.ServiceManager; 37import android.os.UserHandle; 38import android.util.Log; 39import android.view.LayoutInflater; 40import android.view.View; 41import android.view.View.OnClickListener; 42import android.view.ViewGroup; 43import android.widget.ArrayAdapter; 44import android.widget.DateTimeView; 45import android.widget.ImageView; 46import android.widget.ListView; 47import android.widget.TextView; 48import com.android.internal.statusbar.StatusBarNotification; 49 50import java.util.ArrayList; 51import java.util.Comparator; 52import java.util.List; 53 54public class NotificationStation extends SettingsPreferenceFragment { 55 private static final String TAG = NotificationStation.class.getSimpleName(); 56 static final boolean DEBUG = true; 57 private static final String PACKAGE_SCHEME = "package"; 58 private static final boolean SHOW_HISTORICAL_NOTIFICATIONS = true; 59 60 private final PackageReceiver mPackageReceiver = new PackageReceiver(); 61 private PackageManager mPm; 62 private INotificationManager mNoMan; 63 64 private Runnable mRefreshListRunnable = new Runnable() { 65 @Override 66 public void run() { 67 refreshList(); 68 } 69 }; 70 71 private INotificationListener.Stub mListener = new INotificationListener.Stub() { 72 @Override 73 public void onNotificationPosted(StatusBarNotification notification) throws RemoteException { 74 Log.v(TAG, "onNotificationPosted: " + notification); 75 final Handler h = getListView().getHandler(); 76 h.removeCallbacks(mRefreshListRunnable); 77 h.postDelayed(mRefreshListRunnable, 100); 78 } 79 80 @Override 81 public void onNotificationRemoved(StatusBarNotification notification) throws RemoteException { 82 final Handler h = getListView().getHandler(); 83 h.removeCallbacks(mRefreshListRunnable); 84 h.postDelayed(mRefreshListRunnable, 100); 85 } 86 }; 87 88 private NotificationHistoryAdapter mAdapter; 89 private Context mContext; 90 91 private final Comparator<HistoricalNotificationInfo> mNotificationSorter 92 = new Comparator<HistoricalNotificationInfo>() { 93 @Override 94 public int compare(HistoricalNotificationInfo lhs, 95 HistoricalNotificationInfo rhs) { 96 return (int)(rhs.timestamp - lhs.timestamp); 97 } 98 }; 99 100 @Override 101 public void onAttach(Activity activity) { 102 logd("onAttach(%s)", activity.getClass().getSimpleName()); 103 super.onAttach(activity); 104 mContext = activity; 105 mPm = mContext.getPackageManager(); 106 mNoMan = INotificationManager.Stub.asInterface( 107 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 108 try { 109 mNoMan.registerListener(mListener, 110 mContext.getPackageName(), 111 ActivityManager.getCurrentUser()); 112 } catch (RemoteException e) { 113 // well, that didn't work out 114 } 115 } 116 117 @Override 118 public void onCreate(Bundle icicle) { 119 logd("onCreate(%s)", icicle); 120 super.onCreate(icicle); 121 Activity activity = getActivity(); 122 } 123 124 @Override 125 public void onDestroyView() { 126 super.onDestroyView(); 127 } 128 129 @Override 130 public void onActivityCreated(Bundle savedInstanceState) { 131 logd("onActivityCreated(%s)", savedInstanceState); 132 super.onActivityCreated(savedInstanceState); 133 134 ListView listView = getListView(); 135 136// TextView emptyView = (TextView) getView().findViewById(android.R.id.empty); 137// emptyView.setText(R.string.screensaver_settings_disabled_prompt); 138// listView.setEmptyView(emptyView); 139 140 mAdapter = new NotificationHistoryAdapter(mContext); 141 listView.setAdapter(mAdapter); 142 } 143 144 @Override 145 public void onPause() { 146 logd("onPause()"); 147 super.onPause(); 148 mContext.unregisterReceiver(mPackageReceiver); 149 } 150 151 @Override 152 public void onResume() { 153 logd("onResume()"); 154 super.onResume(); 155 refreshList(); 156 157 // listen for package changes 158 IntentFilter filter = new IntentFilter(); 159 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 160 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 161 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 162 filter.addAction(Intent.ACTION_PACKAGE_REPLACED); 163 filter.addDataScheme(PACKAGE_SCHEME); 164 mContext.registerReceiver(mPackageReceiver , filter); 165 } 166 167 private void refreshList() { 168 List<HistoricalNotificationInfo> infos = loadNotifications(); 169 if (infos != null) { 170 logd("adding %d infos", infos.size()); 171 mAdapter.clear(); 172 mAdapter.addAll(infos); 173 mAdapter.sort(mNotificationSorter); 174 } 175 } 176 177 private static void logd(String msg, Object... args) { 178 if (DEBUG) 179 Log.d(TAG, args == null || args.length == 0 ? msg : String.format(msg, args)); 180 } 181 182 private static class HistoricalNotificationInfo { 183 public String pkg; 184 public Drawable pkgicon; 185 public CharSequence pkgname; 186 public Drawable icon; 187 public CharSequence title; 188 public int priority; 189 public int user; 190 public long timestamp; 191 public boolean active; 192 } 193 194 private List<HistoricalNotificationInfo> loadNotifications() { 195 final int currentUserId = ActivityManager.getCurrentUser(); 196 try { 197 StatusBarNotification[] active = mNoMan.getActiveNotifications(mContext.getPackageName()); 198 StatusBarNotification[] dismissed = mNoMan.getHistoricalNotifications(mContext.getPackageName(), 50); 199 200 List<HistoricalNotificationInfo> list 201 = new ArrayList<HistoricalNotificationInfo>(active.length + dismissed.length); 202 203 for (StatusBarNotification[] resultset 204 : new StatusBarNotification[][] { active, dismissed }) { 205 for (StatusBarNotification sbn : resultset) { 206 final HistoricalNotificationInfo info = new HistoricalNotificationInfo(); 207 info.pkg = sbn.pkg; 208 info.user = sbn.getUserId(); 209 info.icon = loadIconDrawable(info.pkg, info.user, sbn.notification.icon); 210 info.pkgicon = loadPackageIconDrawable(info.pkg, info.user); 211 info.pkgname = loadPackageName(info.pkg); 212 if (sbn.notification.extras != null) { 213 info.title = sbn.notification.extras.getString(Notification.EXTRA_TITLE); 214 if (info.title == null || "".equals(info.title)) { 215 info.title = sbn.notification.extras.getString(Notification.EXTRA_TEXT); 216 } 217 } 218 if (info.title == null || "".equals(info.title)) { 219 info.title = sbn.notification.tickerText; 220 } 221 // still nothing? come on, give us something! 222 if (info.title == null || "".equals(info.title)) { 223 info.title = info.pkgname; 224 } 225 info.timestamp = sbn.postTime; 226 info.priority = sbn.notification.priority; 227 logd(" [%d] %s: %s", info.timestamp, info.pkg, info.title); 228 229 info.active = (resultset == active); 230 231 if (info.user == UserHandle.USER_ALL 232 || info.user == currentUserId) { 233 list.add(info); 234 } 235 } 236 } 237 238 return list; 239 } catch (RemoteException e) { 240 e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 241 } 242 return null; 243 } 244 245 private Resources getResourcesForUserPackage(String pkg, int userId) { 246 Resources r = null; 247 248 if (pkg != null) { 249 try { 250 if (userId == UserHandle.USER_ALL) { 251 userId = UserHandle.USER_OWNER; 252 } 253 r = mPm.getResourcesForApplicationAsUser(pkg, userId); 254 } catch (PackageManager.NameNotFoundException ex) { 255 Log.e(TAG, "Icon package not found: " + pkg); 256 return null; 257 } 258 } else { 259 r = mContext.getResources(); 260 } 261 return r; 262 } 263 264 private Drawable loadPackageIconDrawable(String pkg, int userId) { 265 Drawable icon = null; 266 try { 267 icon = mPm.getApplicationIcon(pkg); 268 } catch (PackageManager.NameNotFoundException e) { 269 } 270 271 return icon; 272 } 273 274 private CharSequence loadPackageName(String pkg) { 275 try { 276 ApplicationInfo info = mPm.getApplicationInfo(pkg, 277 PackageManager.GET_UNINSTALLED_PACKAGES); 278 if (info != null) return mPm.getApplicationLabel(info); 279 } catch (PackageManager.NameNotFoundException e) { 280 } 281 return pkg; 282 } 283 284 private Drawable loadIconDrawable(String pkg, int userId, int resId) { 285 Resources r = getResourcesForUserPackage(pkg, userId); 286 287 if (resId == 0) { 288 return null; 289 } 290 291 try { 292 return r.getDrawable(resId); 293 } catch (RuntimeException e) { 294 Log.w(TAG, "Icon not found in " 295 + (pkg != null ? resId : "<system>") 296 + ": " + Integer.toHexString(resId)); 297 } 298 299 return null; 300 } 301 302 private class NotificationHistoryAdapter extends ArrayAdapter<HistoricalNotificationInfo> { 303 private final LayoutInflater mInflater; 304 305 public NotificationHistoryAdapter(Context context) { 306 super(context, 0); 307 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 308 } 309 310 @Override 311 public View getView(int position, View convertView, ViewGroup parent) { 312 final HistoricalNotificationInfo info = getItem(position); 313 logd("getView(%s/%s)", info.pkg, info.title); 314 final View row = convertView != null ? convertView : createRow(parent); 315 row.setTag(info); 316 317 // bind icon 318 if (info.icon != null) { 319 ((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(info.icon); 320 } 321 if (info.pkgicon != null) { 322 ((ImageView) row.findViewById(R.id.pkgicon)).setImageDrawable(info.pkgicon); 323 } 324 325 ((DateTimeView) row.findViewById(R.id.timestamp)).setTime(info.timestamp); 326 327 // bind caption 328 ((TextView) row.findViewById(android.R.id.title)).setText(info.title); 329 330 // app name 331 ((TextView) row.findViewById(R.id.pkgname)).setText(info.pkgname); 332 333 // extra goodies -- not implemented yet 334// ((TextView) row.findViewById(R.id.extra)).setText( 335// ... 336// ); 337 row.findViewById(R.id.extra).setVisibility(View.GONE); 338 339 row.setAlpha(info.active ? 1.0f : 0.5f); 340 341 // set up click handler 342 row.setOnClickListener(new OnClickListener(){ 343 @Override 344 public void onClick(View v) { 345 v.setPressed(true); 346 startApplicationDetailsActivity(info.pkg); 347 }}); 348 349// // bind radio button 350// RadioButton radioButton = (RadioButton) row.findViewById(android.R.id.button1); 351// radioButton.setChecked(dreamInfo.isActive); 352// radioButton.setOnTouchListener(new OnTouchListener() { 353// @Override 354// public boolean onTouch(View v, MotionEvent event) { 355// row.onTouchEvent(event); 356// return false; 357// }}); 358 359 // bind settings button + divider 360// boolean showSettings = info. 361// settingsComponentName != null; 362// View settingsDivider = row.findViewById(R.id.divider); 363// settingsDivider.setVisibility(false ? View.VISIBLE : View.INVISIBLE); 364// 365// ImageView settingsButton = (ImageView) row.findViewById(android.R.id.button2); 366// settingsButton.setVisibility(false ? View.VISIBLE : View.INVISIBLE); 367// settingsButton.setAlpha(info.isActive ? 1f : Utils.DISABLED_ALPHA); 368// settingsButton.setEnabled(info.isActive); 369// settingsButton.setOnClickListener(new OnClickListener(){ 370// @Override 371// public void onClick(View v) { 372// mBackend.launchSettings((DreamInfo) row.getTag()); 373// }}); 374 375 return row; 376 } 377 378 private View createRow(ViewGroup parent) { 379 final View row = mInflater.inflate(R.layout.notification_log_row, parent, false); 380 return row; 381 } 382 383 } 384 385 private void startApplicationDetailsActivity(String packageName) { 386 Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, 387 Uri.fromParts("package", packageName, null)); 388 intent.setComponent(intent.resolveActivity(mPm)); 389 startActivity(intent); 390 } 391 392 private class PackageReceiver extends BroadcastReceiver { 393 @Override 394 public void onReceive(Context context, Intent intent) { 395 logd("PackageReceiver.onReceive"); 396 //refreshList(); 397 } 398 } 399} 400