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; 28 import android.service.notification.NotificationListenerService.Ranking; 29 import android.service.notification.NotificationListenerService.RankingMap; 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; 36 import java.util.Collections; 37 import java.util.Comparator; 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 getNotifications()59 public static List<StatusBarNotification> getNotifications() { 60 return sNotifications; 61 } 62 63 private final Ranking mTmpRanking = new Ranking(); 64 65 private class Delta { 66 final StatusBarNotification mSbn; 67 final RankingMap mRankingMap; 68 Delta(StatusBarNotification sbn, RankingMap rankingMap)69 public Delta(StatusBarNotification sbn, RankingMap rankingMap) { 70 mSbn = sbn; 71 mRankingMap = rankingMap; 72 } 73 } 74 75 private final Comparator<StatusBarNotification> mRankingComparator = 76 new Comparator<StatusBarNotification>() { 77 78 private final Ranking mLhsRanking = new Ranking(); 79 private final Ranking mRhsRanking = new Ranking(); 80 81 @Override 82 public int compare(StatusBarNotification lhs, StatusBarNotification rhs) { 83 mRankingMap.getRanking(lhs.getKey(), mLhsRanking); 84 mRankingMap.getRanking(rhs.getKey(), mRhsRanking); 85 return Integer.compare(mLhsRanking.getRank(), mRhsRanking.getRank()); 86 } 87 }; 88 89 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 90 @Override 91 public void onReceive(Context context, Intent intent) { 92 String key = intent.getStringExtra(EXTRA_KEY); 93 int what = MSG_DISMISS; 94 if (ACTION_LAUNCH.equals(intent.getAction())) { 95 what = MSG_LAUNCH; 96 } 97 Log.d(TAG, "received an action broadcast " + intent.getAction()); 98 if (!TextUtils.isEmpty(key)) { 99 Log.d(TAG, " on " + key); 100 Message.obtain(mHandler, what, key).sendToTarget(); 101 } 102 } 103 }; 104 105 private final Handler mHandler = new Handler() { 106 @Override 107 public void handleMessage(Message msg) { 108 Delta delta = null; 109 if (msg.obj instanceof Delta) { 110 delta = (Delta) msg.obj; 111 } 112 113 switch (msg.what) { 114 case MSG_NOTIFY: 115 Log.i(TAG, "notify: " + delta.mSbn.getKey()); 116 synchronized (sNotifications) { 117 boolean exists = mRankingMap.getRanking(delta.mSbn.getKey(), mTmpRanking); 118 if (!exists) { 119 sNotifications.add(delta.mSbn); 120 } else { 121 int position = mTmpRanking.getRank(); 122 sNotifications.set(position, delta.mSbn); 123 } 124 mRankingMap = delta.mRankingMap; 125 Collections.sort(sNotifications, mRankingComparator); 126 Log.i(TAG, "finish with: " + sNotifications.size()); 127 } 128 LocalBroadcastManager.getInstance(Listener.this) 129 .sendBroadcast(new Intent(ACTION_REFRESH) 130 .putExtra(EXTRA_KEY, delta.mSbn.getKey())); 131 break; 132 133 case MSG_CANCEL: 134 Log.i(TAG, "remove: " + delta.mSbn.getKey()); 135 synchronized (sNotifications) { 136 boolean exists = mRankingMap.getRanking(delta.mSbn.getKey(), mTmpRanking); 137 if (exists) { 138 sNotifications.remove(mTmpRanking.getRank()); 139 } 140 mRankingMap = delta.mRankingMap; 141 Collections.sort(sNotifications, mRankingComparator); 142 } 143 LocalBroadcastManager.getInstance(Listener.this) 144 .sendBroadcast(new Intent(ACTION_REFRESH)); 145 break; 146 147 case MSG_ORDER: 148 Log.i(TAG, "reorder"); 149 synchronized (sNotifications) { 150 mRankingMap = delta.mRankingMap; 151 Collections.sort(sNotifications, mRankingComparator); 152 } 153 LocalBroadcastManager.getInstance(Listener.this) 154 .sendBroadcast(new Intent(ACTION_REFRESH)); 155 break; 156 157 case MSG_STARTUP: 158 fetchActive(); 159 Log.i(TAG, "start with: " + sNotifications.size() + " notifications."); 160 LocalBroadcastManager.getInstance(Listener.this) 161 .sendBroadcast(new Intent(ACTION_REFRESH)); 162 break; 163 164 case MSG_DISMISS: 165 if (msg.obj instanceof String) { 166 final String key = (String) msg.obj; 167 mRankingMap.getRanking(key, mTmpRanking); 168 StatusBarNotification sbn = sNotifications.get(mTmpRanking.getRank()); 169 if ((sbn.getNotification().flags & Notification.FLAG_AUTO_CANCEL) != 0 && 170 sbn.getNotification().contentIntent != null) { 171 try { 172 sbn.getNotification().contentIntent.send(); 173 } catch (PendingIntent.CanceledException e) { 174 Log.d(TAG, "failed to send intent for " + sbn.getKey(), e); 175 } 176 } 177 cancelNotification(key); 178 } 179 break; 180 181 case MSG_LAUNCH: 182 if (msg.obj instanceof String) { 183 final String key = (String) msg.obj; 184 mRankingMap.getRanking(key, mTmpRanking); 185 StatusBarNotification sbn = sNotifications.get(mTmpRanking.getRank()); 186 if (sbn.getNotification().contentIntent != null) { 187 try { 188 sbn.getNotification().contentIntent.send(); 189 } catch (PendingIntent.CanceledException e) { 190 Log.d(TAG, "failed to send intent for " + sbn.getKey(), e); 191 } 192 } 193 if ((sbn.getNotification().flags & Notification.FLAG_AUTO_CANCEL) != 0) { 194 cancelNotification(key); 195 } 196 } 197 break; 198 } 199 } 200 }; 201 202 private RankingMap mRankingMap; 203 204 @Override onCreate()205 public void onCreate() { 206 super.onCreate(); 207 Log.d(TAG, "registering broadcast listener"); 208 final IntentFilter intentFilter = new IntentFilter(); 209 intentFilter.addAction(ACTION_DISMISS); 210 intentFilter.addAction(ACTION_LAUNCH); 211 LocalBroadcastManager.getInstance(this).registerReceiver(mBroadcastReceiver, intentFilter); 212 } 213 214 @Override onDestroy()215 public void onDestroy() { 216 LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver); 217 super.onDestroy(); 218 } 219 220 @Override onListenerConnected()221 public void onListenerConnected() { 222 Message.obtain(mHandler, MSG_STARTUP).sendToTarget(); 223 } 224 225 @Override onNotificationRankingUpdate(RankingMap rankingMap)226 public void onNotificationRankingUpdate(RankingMap rankingMap) { 227 Message.obtain(mHandler, MSG_ORDER, 228 new Delta(null, rankingMap)).sendToTarget(); 229 } 230 231 @Override onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap)232 public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) { 233 Message.obtain(mHandler, MSG_NOTIFY, 234 new Delta(sbn, rankingMap)).sendToTarget(); 235 } 236 237 @Override onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap)238 public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) { 239 Message.obtain(mHandler, MSG_CANCEL, 240 new Delta(sbn, rankingMap)).sendToTarget(); 241 } 242 fetchActive()243 private void fetchActive() { 244 mRankingMap = getCurrentRanking(); 245 sNotifications = new ArrayList<StatusBarNotification>(); 246 for (StatusBarNotification sbn : getActiveNotifications()) { 247 sNotifications.add(sbn); 248 } 249 Collections.sort(sNotifications, mRankingComparator); 250 } 251 } 252