Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2014 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 | package com.android.example.notificationlistener; |
| 17 | |
| 18 | |
| 19 | import android.app.Notification; |
| 20 | import android.app.PendingIntent; |
| 21 | import android.content.BroadcastReceiver; |
| 22 | import android.content.Context; |
| 23 | import android.content.Intent; |
| 24 | import android.content.IntentFilter; |
| 25 | import android.os.Handler; |
| 26 | import android.os.Message; |
| 27 | import android.service.notification.NotificationListenerService; |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 28 | import android.service.notification.NotificationListenerService.Ranking; |
| 29 | import android.service.notification.NotificationListenerService.RankingMap; |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 30 | import android.service.notification.StatusBarNotification; |
| 31 | import android.support.v4.content.LocalBroadcastManager; |
| 32 | import android.text.TextUtils; |
| 33 | import android.util.Log; |
| 34 | |
| 35 | import java.util.ArrayList; |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 36 | import java.util.Collections; |
Chris Wren | a02d4fd | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 37 | import java.util.Comparator; |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 38 | import java.util.List; |
| 39 | |
| 40 | public class Listener extends NotificationListenerService { |
| 41 | private static final String TAG = "SampleListener"; |
| 42 | |
| 43 | // Message tags |
| 44 | private static final int MSG_NOTIFY = 1; |
| 45 | private static final int MSG_CANCEL = 2; |
| 46 | private static final int MSG_STARTUP = 3; |
| 47 | private static final int MSG_ORDER = 4; |
| 48 | private static final int MSG_DISMISS = 5; |
| 49 | private static final int MSG_LAUNCH = 6; |
| 50 | private static final int PAGE = 10; |
| 51 | |
| 52 | static final String ACTION_DISMISS = "com.android.example.notificationlistener.DISMISS"; |
| 53 | static final String ACTION_LAUNCH = "com.android.example.notificationlistener.LAUNCH"; |
| 54 | static final String ACTION_REFRESH = "com.android.example.notificationlistener.REFRESH"; |
| 55 | static final String EXTRA_KEY = "key"; |
| 56 | |
| 57 | private static ArrayList<StatusBarNotification> sNotifications; |
| 58 | |
| 59 | public static List<StatusBarNotification> getNotifications() { |
| 60 | return sNotifications; |
| 61 | } |
| 62 | |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 63 | private class Delta { |
| 64 | final StatusBarNotification mSbn; |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 65 | final RankingMap mRankingMap; |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 66 | |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 67 | public Delta(StatusBarNotification sbn, RankingMap rankingMap) { |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 68 | mSbn = sbn; |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 69 | mRankingMap = rankingMap; |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 70 | } |
| 71 | } |
| 72 | |
Chris Wren | a02d4fd | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 73 | private final Comparator<StatusBarNotification> mRankingComparator = |
| 74 | new Comparator<StatusBarNotification>() { |
| 75 | @Override |
| 76 | public int compare(StatusBarNotification lhs, StatusBarNotification rhs) { |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 77 | Ranking lhsRanking = mRankingMap.getRanking(lhs.getKey()); |
| 78 | Ranking rhsRanking = mRankingMap.getRanking(rhs.getKey()); |
| 79 | return Integer.compare(lhsRanking.getRank(), rhsRanking.getRank()); |
Chris Wren | a02d4fd | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 80 | } |
| 81 | }; |
| 82 | |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 83 | private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { |
| 84 | @Override |
| 85 | public void onReceive(Context context, Intent intent) { |
| 86 | String key = intent.getStringExtra(EXTRA_KEY); |
| 87 | int what = MSG_DISMISS; |
| 88 | if (ACTION_LAUNCH.equals(intent.getAction())) { |
| 89 | what = MSG_LAUNCH; |
| 90 | } |
| 91 | Log.d(TAG, "received an action broadcast " + intent.getAction()); |
| 92 | if (!TextUtils.isEmpty(key)) { |
| 93 | Log.d(TAG, " on " + key); |
| 94 | Message.obtain(mHandler, what, key).sendToTarget(); |
| 95 | } |
| 96 | } |
| 97 | }; |
| 98 | |
Chris Wren | a02d4fd | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 99 | private final Handler mHandler = new Handler() { |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 100 | @Override |
| 101 | public void handleMessage(Message msg) { |
| 102 | Delta delta = null; |
| 103 | if (msg.obj instanceof Delta) { |
| 104 | delta = (Delta) msg.obj; |
| 105 | } |
| 106 | switch (msg.what) { |
| 107 | case MSG_NOTIFY: |
| 108 | Log.i(TAG, "notify: " + delta.mSbn.getKey()); |
| 109 | synchronized (sNotifications) { |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 110 | Ranking ranking = mRankingMap.getRanking(delta.mSbn.getKey()); |
| 111 | if (ranking == null) { |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 112 | sNotifications.add(delta.mSbn); |
| 113 | } else { |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 114 | int position = ranking.getRank(); |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 115 | sNotifications.set(position, delta.mSbn); |
| 116 | } |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 117 | mRankingMap = delta.mRankingMap; |
Chris Wren | a02d4fd | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 118 | Collections.sort(sNotifications, mRankingComparator); |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 119 | Log.i(TAG, "finish with: " + sNotifications.size()); |
| 120 | } |
| 121 | LocalBroadcastManager.getInstance(Listener.this) |
| 122 | .sendBroadcast(new Intent(ACTION_REFRESH) |
| 123 | .putExtra(EXTRA_KEY, delta.mSbn.getKey())); |
| 124 | break; |
| 125 | |
| 126 | case MSG_CANCEL: |
| 127 | Log.i(TAG, "remove: " + delta.mSbn.getKey()); |
| 128 | synchronized (sNotifications) { |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 129 | Ranking ranking = mRankingMap.getRanking(delta.mSbn.getKey()); |
| 130 | int position = ranking.getRank(); |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 131 | if (position != -1) { |
| 132 | sNotifications.remove(position); |
| 133 | } |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 134 | mRankingMap = delta.mRankingMap; |
Chris Wren | a02d4fd | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 135 | Collections.sort(sNotifications, mRankingComparator); |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 136 | } |
| 137 | LocalBroadcastManager.getInstance(Listener.this) |
| 138 | .sendBroadcast(new Intent(ACTION_REFRESH)); |
| 139 | break; |
| 140 | |
| 141 | case MSG_ORDER: |
| 142 | Log.i(TAG, "reorder"); |
| 143 | synchronized (sNotifications) { |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 144 | mRankingMap = delta.mRankingMap; |
Chris Wren | a02d4fd | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 145 | Collections.sort(sNotifications, mRankingComparator); |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 146 | } |
| 147 | LocalBroadcastManager.getInstance(Listener.this) |
| 148 | .sendBroadcast(new Intent(ACTION_REFRESH)); |
| 149 | break; |
| 150 | |
| 151 | case MSG_STARTUP: |
| 152 | fetchActive(); |
| 153 | Log.i(TAG, "start with: " + sNotifications.size() + " notifications."); |
| 154 | LocalBroadcastManager.getInstance(Listener.this) |
| 155 | .sendBroadcast(new Intent(ACTION_REFRESH)); |
| 156 | break; |
| 157 | |
| 158 | case MSG_DISMISS: |
| 159 | if (msg.obj instanceof String) { |
| 160 | final String key = (String) msg.obj; |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 161 | Ranking ranking = mRankingMap.getRanking(key); |
| 162 | StatusBarNotification sbn = sNotifications.get(ranking.getRank()); |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 163 | if ((sbn.getNotification().flags & Notification.FLAG_AUTO_CANCEL) != 0 && |
| 164 | sbn.getNotification().contentIntent != null) { |
| 165 | try { |
| 166 | sbn.getNotification().contentIntent.send(); |
| 167 | } catch (PendingIntent.CanceledException e) { |
| 168 | Log.d(TAG, "failed to send intent for " + sbn.getKey(), e); |
| 169 | } |
| 170 | } |
| 171 | cancelNotification(key); |
| 172 | } |
| 173 | break; |
| 174 | |
| 175 | case MSG_LAUNCH: |
| 176 | if (msg.obj instanceof String) { |
| 177 | final String key = (String) msg.obj; |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 178 | Ranking ranking = mRankingMap.getRanking(delta.mSbn.getKey()); |
| 179 | int position = ranking.getRank(); |
| 180 | StatusBarNotification sbn = sNotifications.get(position); |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 181 | if (sbn.getNotification().contentIntent != null) { |
| 182 | try { |
| 183 | sbn.getNotification().contentIntent.send(); |
| 184 | } catch (PendingIntent.CanceledException e) { |
| 185 | Log.d(TAG, "failed to send intent for " + sbn.getKey(), e); |
| 186 | } |
| 187 | } |
| 188 | if ((sbn.getNotification().flags & Notification.FLAG_AUTO_CANCEL) != 0) { |
| 189 | cancelNotification(key); |
| 190 | } |
| 191 | } |
| 192 | break; |
| 193 | } |
| 194 | } |
| 195 | }; |
| 196 | |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 197 | private RankingMap mRankingMap; |
Chris Wren | a02d4fd | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 198 | |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 199 | @Override |
| 200 | public void onCreate() { |
| 201 | super.onCreate(); |
| 202 | Log.d(TAG, "registering broadcast listener"); |
| 203 | final IntentFilter intentFilter = new IntentFilter(); |
| 204 | intentFilter.addAction(ACTION_DISMISS); |
| 205 | intentFilter.addAction(ACTION_LAUNCH); |
| 206 | LocalBroadcastManager.getInstance(this).registerReceiver(mBroadcastReceiver, intentFilter); |
| 207 | } |
| 208 | |
| 209 | @Override |
| 210 | public void onDestroy() { |
| 211 | LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver); |
| 212 | super.onDestroy(); |
| 213 | } |
| 214 | |
| 215 | @Override |
Christoph Studer | 2b5b44a | 2014-05-21 21:23:45 +0200 | [diff] [blame] | 216 | public void onListenerConnected() { |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 217 | Message.obtain(mHandler, MSG_STARTUP).sendToTarget(); |
| 218 | } |
| 219 | |
| 220 | @Override |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 221 | public void onNotificationRankingUpdate(RankingMap rankingMap) { |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 222 | Message.obtain(mHandler, MSG_ORDER, |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 223 | new Delta(null, rankingMap)).sendToTarget(); |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 224 | } |
| 225 | |
| 226 | @Override |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 227 | public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) { |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 228 | Message.obtain(mHandler, MSG_NOTIFY, |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 229 | new Delta(sbn, rankingMap)).sendToTarget(); |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 230 | } |
| 231 | |
| 232 | @Override |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 233 | public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) { |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 234 | Message.obtain(mHandler, MSG_CANCEL, |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 235 | new Delta(sbn, rankingMap)).sendToTarget(); |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 236 | } |
| 237 | |
| 238 | private void fetchActive() { |
Christoph Studer | 1390028 | 2014-06-06 11:18:52 +0200 | [diff] [blame^] | 239 | mRankingMap = getCurrentRanking(); |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 240 | sNotifications = new ArrayList<StatusBarNotification>(); |
Christoph Studer | 2b5b44a | 2014-05-21 21:23:45 +0200 | [diff] [blame] | 241 | for (StatusBarNotification sbn : getActiveNotifications()) { |
| 242 | sNotifications.add(sbn); |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 243 | } |
Chris Wren | a02d4fd | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 244 | Collections.sort(sNotifications, mRankingComparator); |
Chris Wren | f715fd9 | 2014-05-07 17:44:21 -0400 | [diff] [blame] | 245 | } |
| 246 | } |