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