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