1 // Copyright (c) 2014 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.xwalk.core.internal;
7 import java.util.HashMap;
9 import android.app.Notification;
10 import android.app.NotificationManager;
11 import android.app.PendingIntent;
12 import android.content.BroadcastReceiver;
13 import android.content.Context;
14 import android.content.Intent;
15 import android.content.IntentFilter;
16 import android.graphics.Bitmap;
17 import android.os.Build.VERSION;
18 import android.os.Build.VERSION_CODES;
19 import android.util.AndroidRuntimeException;
20 import android.util.Log;
22 import org.xwalk.core.internal.XWalkContentsClientBridge;
23 import org.xwalk.core.internal.XWalkNotificationService;
24 import org.xwalk.core.internal.XWalkViewInternal;
29 public class XWalkNotificationServiceImpl implements XWalkNotificationService {
30 private static final String TAG = "XWalkNotificationServiceImpl";
32 private static final String XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX = ".notification.click";
33 private static final String XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX = ".notification.close";
34 private static final String XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID = "xwalk.NOTIFICATION_ID";
35 private static final String XWALK_INTENT_EXTRA_KEY_DELEGATE = "xwalk.DELEGATE";
36 private static final String XWALK_INTENT_CATEGORY_NOTIFICATION_PREFIX = "notification_";
38 private class WebNotification {
43 public Integer mNotificationId;
44 public String mReplaceId;
45 public Notification.Builder mBuilder;
46 public Integer mMessageNum;
49 private Context mContext;
50 private XWalkContentsClientBridge mBridge;
51 private XWalkViewInternal mView;
52 private NotificationManager mNotificationManager;
53 private BroadcastReceiver mNotificationCloseReceiver;
54 private HashMap<Integer, WebNotification> mExistNotificationIds;
55 private HashMap<String, WebNotification> mExistReplaceIds;
57 public XWalkNotificationServiceImpl(Context context, XWalkViewInternal view) {
60 mNotificationManager =
61 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
62 // Cancel all exist notifications at startup time. To avoid receiving legacy pendingIntents.
63 mNotificationManager.cancelAll();
65 mNotificationCloseReceiver = new BroadcastReceiver() {
67 public void onReceive(Context context, Intent intent) {
68 mView.onNewIntent(intent);
72 mExistNotificationIds = new HashMap<Integer, WebNotification>();
73 mExistReplaceIds = new HashMap<String, WebNotification>();
76 private static String getCategoryFromNotificationId(int id) {
77 return XWALK_INTENT_CATEGORY_NOTIFICATION_PREFIX + id;
81 public void setBridge(XWalkContentsClientBridge bridge) {
86 public void shutdown() {
87 if (!mExistNotificationIds.isEmpty()) {
94 public boolean maybeHandleIntent(Intent intent) {
95 if (intent.getAction() == null) return false;
96 int notificationId = intent.getIntExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, -1);
97 long delegate = intent.getLongExtra(XWALK_INTENT_EXTRA_KEY_DELEGATE, -1);
98 if (notificationId <= 0) return false;
99 if (intent.getAction().equals(
100 mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX)) {
101 onNotificationClose(notificationId, true, delegate);
103 } else if (intent.getAction().equals(
104 mView.getActivity().getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX)) {
105 onNotificationClick(notificationId, delegate);
112 @SuppressWarnings("deprecation")
113 public void updateNotificationIcon(int notificationId, Bitmap icon) {
114 WebNotification webNotification = mExistNotificationIds.get(notificationId);
115 if (webNotification == null) {
119 int originalWidth = icon.getWidth();
120 int originalHeight = icon.getHeight();
121 if (originalWidth == 0 || originalHeight == 0) {
125 int targetWidth = mContext.getResources().getDimensionPixelSize(
126 android.R.dimen.notification_large_icon_width);
127 int targetHeight = mContext.getResources().getDimensionPixelSize(
128 android.R.dimen.notification_large_icon_height);
130 if (originalWidth > targetWidth && originalHeight > targetHeight) {
131 if (originalWidth * targetHeight > originalHeight * targetWidth) {
132 targetHeight = originalHeight * targetWidth / originalWidth;
134 targetWidth = originalWidth * targetHeight / originalHeight;
138 Notification.Builder builder = webNotification.mBuilder;
139 builder.setLargeIcon(Bitmap.createScaledBitmap(icon, targetWidth, targetHeight, true));
141 doShowNotification(notificationId,
142 VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN ? builder.build() : builder.getNotification());
146 @SuppressWarnings("deprecation")
147 public void showNotification(String title, String message, String replaceId,
148 int notificationId, long delegate) {
149 Notification.Builder builder;
151 if (!replaceId.isEmpty() && mExistReplaceIds.containsKey(replaceId)) {
152 WebNotification webNotification = mExistReplaceIds.get(replaceId);
153 notificationId = webNotification.mNotificationId;
154 builder = webNotification.mBuilder;
155 builder.setNumber(++webNotification.mMessageNum);
157 builder = new Notification.Builder(mContext.getApplicationContext())
158 .setAutoCancel(true);
160 WebNotification webNotification = new WebNotification();
161 webNotification.mNotificationId = notificationId;
162 webNotification.mReplaceId = replaceId;
163 webNotification.mBuilder = builder;
165 mExistNotificationIds.put(notificationId, webNotification);
166 if (!replaceId.isEmpty()) {
167 mExistReplaceIds.put(replaceId, webNotification);
171 builder.setContentTitle(title);
172 builder.setContentText(message);
174 int iconRes = mContext.getApplicationInfo().icon;
176 iconRes = android.R.drawable.sym_def_app_icon;
178 builder.setSmallIcon(iconRes);
180 Context activity = mView.getActivity();
181 String category = getCategoryFromNotificationId(notificationId);
183 Intent clickIntent = new Intent(activity, activity.getClass())
184 .setAction(activity.getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX)
185 .putExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, notificationId)
186 .putExtra(XWALK_INTENT_EXTRA_KEY_DELEGATE, delegate)
187 .setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | Intent.FLAG_ACTIVITY_SINGLE_TOP)
188 .addCategory(category);
190 Intent closeIntent = new Intent(activity.getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX)
191 .putExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, notificationId)
192 .putExtra(XWALK_INTENT_EXTRA_KEY_DELEGATE, delegate)
193 .addCategory(category);
195 builder.setContentIntent(PendingIntent.getActivity(
196 activity, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT));
197 builder.setDeleteIntent(PendingIntent.getBroadcast(
198 activity, 0, closeIntent, PendingIntent.FLAG_UPDATE_CURRENT));
200 doShowNotification(notificationId,
201 VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN ? builder.build() : builder.getNotification());
202 notificationChanged();
203 onNotificationShown(notificationId, delegate);
207 public void cancelNotification(int notificationId, long delegate) {
208 mNotificationManager.cancel(notificationId);
209 onNotificationClose(notificationId, false, delegate);
212 public void doShowNotification(int notificationId, Notification notification) {
213 mNotificationManager.notify(notificationId, notification);
216 public void onNotificationShown(int notificationId, long delegate) {
217 WebNotification webNotification = mExistNotificationIds.get(notificationId);
218 if (webNotification == null) {
222 if (mBridge != null) {
223 mBridge.notificationDisplayed(delegate);
227 public void onNotificationClick(int notificationId, long delegate) {
228 WebNotification webNotification = mExistNotificationIds.get(notificationId);
229 if (webNotification == null) {
233 mExistNotificationIds.remove(notificationId);
234 mExistReplaceIds.remove(webNotification.mReplaceId);
236 notificationChanged();
237 if (mBridge != null) {
238 mBridge.notificationClicked(notificationId, delegate);
242 public void onNotificationClose(
243 int notificationId, boolean byUser, long delegate) {
244 WebNotification webNotification = mExistNotificationIds.get(notificationId);
245 if (webNotification == null) {
249 mExistNotificationIds.remove(notificationId);
250 mExistReplaceIds.remove(webNotification.mReplaceId);
252 notificationChanged();
253 if (mBridge != null) {
254 mBridge.notificationClosed(notificationId, byUser, delegate);
258 private void notificationChanged() {
259 if (mExistNotificationIds.isEmpty()) {
260 Log.i(TAG, "notifications are all cleared," +
261 "unregister broadcast receiver for close pending intent");
262 unregisterReceiver();
268 private void registerReceiver() {
269 IntentFilter filter = new IntentFilter(
270 mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX);
272 for(Integer id : mExistNotificationIds.keySet()) {
273 filter.addCategory(getCategoryFromNotificationId(id));
277 mView.getActivity().registerReceiver(mNotificationCloseReceiver, filter);
278 } catch (AndroidRuntimeException e) {
279 //FIXME(wang16): The exception will happen when there are multiple xwalkviews in one activity.
280 // Remove it after notification service supports multi-views.
281 Log.w(TAG, e.getLocalizedMessage());
285 private void unregisterReceiver() {
286 mView.getActivity().unregisterReceiver(mNotificationCloseReceiver);