Home | History | Annotate | Download | only in settings
      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
    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
    120     public void onCreate(Bundle icicle) {
    121         logd("onCreate(%s)", icicle);
    122         super.onCreate(icicle);
    123         Activity activity = getActivity();
    124     }
    125 
    126     @Override
    127     public void onDestroyView() {
    128         super.onDestroyView();
    129     }
    130 
    131     @Override
    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
    147     public void onPause() {
    148         logd("onPause()");
    149         super.onPause();
    150         mContext.unregisterReceiver(mPackageReceiver);
    151     }
    152 
    153     @Override
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    307         public NotificationHistoryAdapter(Context context) {
    308             super(context, 0);
    309             mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    310         }
    311 
    312         @Override
    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 
    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 
    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
    396         public void onReceive(Context context, Intent intent) {
    397             logd("PackageReceiver.onReceive");
    398             //refreshList();
    399         }
    400     }
    401 }
    402