1 /* 2 * Copyright (C) 2007-2008 Esmertec AG. 3 * Copyright (C) 2007-2008 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.im.service; 19 20 import java.util.HashMap; 21 22 import android.app.Notification; 23 import android.app.NotificationManager; 24 import android.app.PendingIntent; 25 import android.content.ContentUris; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.net.Uri; 29 import android.os.Handler; 30 import android.os.SystemClock; 31 import android.text.TextUtils; 32 import android.util.Log; 33 34 import com.android.im.R; 35 import com.android.im.app.ContactListActivity; 36 import com.android.im.app.NewChatActivity; 37 import com.android.im.provider.Imps; 38 39 public class StatusBarNotifier { 40 private static final boolean DBG = false; 41 42 private static final long SUPPRESS_SOUND_INTERVAL_MS = 3000L; 43 44 static final long[] VIBRATE_PATTERN = new long[] {0, 250, 250, 250}; 45 46 private Context mContext; 47 private NotificationManager mNotificationManager; 48 49 private HashMap<Long, Imps.ProviderSettings.QueryMap> mSettings; 50 private Handler mHandler; 51 private HashMap<Long, NotificationInfo> mNotificationInfos; 52 private long mLastSoundPlayedMs; 53 54 public StatusBarNotifier(Context context) { 55 mContext = context; 56 mNotificationManager = (NotificationManager) context.getSystemService( 57 Context.NOTIFICATION_SERVICE); 58 mSettings = new HashMap<Long, Imps.ProviderSettings.QueryMap>(); 59 mHandler = new Handler(); 60 mNotificationInfos = new HashMap<Long, NotificationInfo>(); 61 } 62 63 public void onServiceStop() { 64 for(Imps.ProviderSettings.QueryMap queryMap : mSettings.values()) { 65 queryMap.close(); 66 } 67 } 68 69 public void notifyChat(long providerId, long accountId, long chatId, 70 String username, String nickname, String msg, boolean lightWeightNotify) { 71 if (!isNotificationEnabled(providerId)) { 72 if (DBG) log("notification for chat " + username + " is not enabled"); 73 return; 74 } 75 76 String title = nickname; 77 String snippet = nickname + ": " + msg; 78 Intent intent = new Intent(Intent.ACTION_VIEW, 79 ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, chatId)); 80 intent.addCategory(com.android.im.app.ImApp.IMPS_CATEGORY); 81 notify(username, title, snippet, msg, providerId, accountId, intent, lightWeightNotify); 82 } 83 84 public void notifySubscriptionRequest(long providerId, long accountId, 85 long contactId, String username, String nickname) { 86 if (!isNotificationEnabled(providerId)) { 87 if (DBG) log("notification for subscription request " + username + " is not enabled"); 88 return; 89 } 90 String title = nickname; 91 String message = mContext.getString(R.string.subscription_notify_text, nickname); 92 Intent intent = new Intent(ImServiceConstants.ACTION_MANAGE_SUBSCRIPTION, 93 ContentUris.withAppendedId(Imps.Contacts.CONTENT_URI, contactId)); 94 intent.putExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, providerId); 95 intent.putExtra(ImServiceConstants.EXTRA_INTENT_FROM_ADDRESS, username); 96 notify(username, title, message, message, providerId, accountId, intent, false); 97 } 98 99 public void notifyGroupInvitation(long providerId, long accountId, 100 long invitationId, String username) { 101 102 Intent intent = new Intent(Intent.ACTION_VIEW, 103 ContentUris.withAppendedId(Imps.Invitation.CONTENT_URI, invitationId)); 104 105 String title = mContext.getString(R.string.notify_groupchat_label); 106 String message = mContext.getString( 107 R.string.group_chat_invite_notify_text, username); 108 notify(username, title, message, message, providerId, accountId, intent, false); 109 } 110 111 public void dismissNotifications(long providerId) { 112 synchronized (mNotificationInfos) { 113 NotificationInfo info = mNotificationInfos.get(providerId); 114 if (info != null) { 115 mNotificationManager.cancel(info.computeNotificationId()); 116 mNotificationInfos.remove(providerId); 117 } 118 } 119 } 120 121 public void dismissChatNotification(long providerId, String username) { 122 NotificationInfo info; 123 boolean removed; 124 synchronized (mNotificationInfos) { 125 info = mNotificationInfos.get(providerId); 126 if (info == null) { 127 return; 128 } 129 removed = info.removeItem(username); 130 } 131 132 if (removed) { 133 if (info.getMessage() == null) { 134 if (DBG) log("dismissChatNotification: removed notification for " + providerId); 135 mNotificationManager.cancel(info.computeNotificationId()); 136 } else { 137 if (DBG) { 138 log("cancelNotify: new notification" + 139 " mTitle=" + info.getTitle() + 140 " mMessage=" + info.getMessage() + 141 " mIntent=" + info.getIntent()); 142 } 143 mNotificationManager.notify(info.computeNotificationId(), 144 info.createNotification("", true)); 145 } 146 } 147 } 148 149 private Imps.ProviderSettings.QueryMap getProviderSettings(long providerId) { 150 Imps.ProviderSettings.QueryMap res = mSettings.get(providerId); 151 if (res == null) { 152 res = new Imps.ProviderSettings.QueryMap(mContext.getContentResolver(), 153 providerId, true, mHandler); 154 mSettings.put(providerId, res); 155 } 156 return res; 157 } 158 159 private boolean isNotificationEnabled(long providerId) { 160 Imps.ProviderSettings.QueryMap settings = getProviderSettings(providerId); 161 return settings.getEnableNotification(); 162 } 163 164 private void notify(String sender, String title, String tickerText, String message, 165 long providerId, long accountId, Intent intent, boolean lightWeightNotify) { 166 NotificationInfo info; 167 synchronized (mNotificationInfos) { 168 info = mNotificationInfos.get(providerId); 169 if (info == null) { 170 info = new NotificationInfo(providerId, accountId); 171 mNotificationInfos.put(providerId, info); 172 } 173 info.addItem(sender, title, message, intent); 174 } 175 176 mNotificationManager.notify(info.computeNotificationId(), 177 info.createNotification(tickerText, lightWeightNotify)); 178 } 179 180 private void setRinger(long providerId, Notification notification) { 181 Imps.ProviderSettings.QueryMap settings = getProviderSettings(providerId); 182 String ringtoneUri = settings.getRingtoneURI(); 183 boolean vibrate = settings.getVibrate(); 184 185 notification.sound = TextUtils.isEmpty(ringtoneUri) ? null : Uri.parse(ringtoneUri); 186 if (notification.sound != null) { 187 mLastSoundPlayedMs = SystemClock.elapsedRealtime(); 188 } 189 190 if (DBG) log("setRinger: notification.sound = " + notification.sound); 191 192 if (vibrate) { 193 notification.defaults |= Notification.DEFAULT_VIBRATE; 194 if (DBG) log("setRinger: defaults |= vibrate"); 195 } 196 } 197 198 class NotificationInfo { 199 class Item { 200 String mTitle; 201 String mMessage; 202 Intent mIntent; 203 204 public Item(String title, String message, Intent intent) { 205 mTitle = title; 206 mMessage = message; 207 mIntent = intent; 208 } 209 } 210 211 private HashMap<String, Item> mItems; 212 213 private long mProviderId; 214 private long mAccountId; 215 216 public NotificationInfo(long providerId, long accountId) { 217 mProviderId = providerId; 218 mAccountId = accountId; 219 mItems = new HashMap<String, Item>(); 220 } 221 222 public int computeNotificationId() { 223 return (int)mProviderId; 224 } 225 226 public synchronized void addItem(String sender, String title, String message, Intent intent) { 227 Item item = mItems.get(sender); 228 if (item == null) { 229 item = new Item(title, message, intent); 230 mItems.put(sender, item); 231 } else { 232 item.mTitle = title; 233 item.mMessage = message; 234 item.mIntent = intent; 235 } 236 } 237 238 public synchronized boolean removeItem(String sender) { 239 Item item = mItems.remove(sender); 240 if (item != null) { 241 return true; 242 } 243 return false; 244 } 245 246 public Notification createNotification(String tickerText, boolean lightWeightNotify) { 247 Notification notification = new Notification( 248 android.R.drawable.stat_notify_chat, 249 lightWeightNotify ? null : tickerText, 250 System.currentTimeMillis()); 251 252 Intent intent = getIntent(); 253 254 notification.setLatestEventInfo(mContext, getTitle(), getMessage(), 255 PendingIntent.getActivity(mContext, 0, intent, 0)); 256 notification.flags |= Notification.FLAG_AUTO_CANCEL; 257 if (!(lightWeightNotify || shouldSuppressSoundNotification())) { 258 setRinger(mProviderId, notification); 259 } 260 return notification; 261 } 262 263 private Intent getDefaultIntent() { 264 Intent intent = new Intent(Intent.ACTION_VIEW); 265 intent.setType(Imps.Contacts.CONTENT_TYPE); 266 intent.setClass(mContext, ContactListActivity.class); 267 intent.putExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, mAccountId); 268 269 return intent; 270 } 271 272 private Intent getMultipleNotificationIntent() { 273 Intent intent = new Intent(Intent.ACTION_VIEW); 274 intent.setClass(mContext, NewChatActivity.class); 275 intent.putExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, mProviderId); 276 intent.putExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, mAccountId); 277 intent.putExtra(ImServiceConstants.EXTRA_INTENT_SHOW_MULTIPLE, true); 278 return intent; 279 } 280 281 public String getTitle() { 282 int count = mItems.size(); 283 if (count == 0) { 284 return null; 285 } else if (count == 1) { 286 Item item = mItems.values().iterator().next(); 287 return item.mTitle; 288 } else { 289 return mContext.getString(R.string.newMessages_label, 290 Imps.Provider.getProviderNameForId(mContext.getContentResolver(), mProviderId)); 291 } 292 } 293 294 public String getMessage() { 295 int count = mItems.size(); 296 if (count == 0) { 297 return null; 298 } else if (count == 1) { 299 Item item = mItems.values().iterator().next(); 300 return item.mMessage; 301 } else { 302 return mContext.getString(R.string.num_unread_chats, count); 303 } 304 } 305 306 public Intent getIntent() { 307 int count = mItems.size(); 308 if (count == 0) { 309 return getDefaultIntent(); 310 } else if (count == 1) { 311 Item item = mItems.values().iterator().next(); 312 return item.mIntent; 313 } else { 314 return getMultipleNotificationIntent(); 315 } 316 } 317 } 318 319 private static void log(String msg) { 320 Log.d(RemoteImService.TAG, "[StatusBarNotify] " + msg); 321 } 322 323 private boolean shouldSuppressSoundNotification() { 324 return (SystemClock.elapsedRealtime() - mLastSoundPlayedMs < SUPPRESS_SOUND_INTERVAL_MS); 325 } 326 } 327