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_CATEGORY_NOTIFICATION_PREFIX = "notification_";
37 private class WebNotification {
42 public Integer mNotificationId;
43 public String mReplaceId;
44 public Notification.Builder mBuilder;
45 public Integer mMessageNum;
48 private Context mContext;
49 private XWalkContentsClientBridge mBridge;
50 private XWalkViewInternal mView;
51 private NotificationManager mNotificationManager;
52 private BroadcastReceiver mNotificationCloseReceiver;
53 private HashMap<Integer, WebNotification> mExistNotificationIds;
54 private HashMap<String, WebNotification> mExistReplaceIds;
56 public XWalkNotificationServiceImpl(Context context, XWalkViewInternal view) {
59 mNotificationManager =
60 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
61 // Cancel all exist notifications at startup time. To avoid receiving legacy pendingIntents.
62 mNotificationManager.cancelAll();
64 mNotificationCloseReceiver = new BroadcastReceiver() {
66 public void onReceive(Context context, Intent intent) {
67 mView.onNewIntent(intent);
71 mExistNotificationIds = new HashMap<Integer, WebNotification>();
72 mExistReplaceIds = new HashMap<String, WebNotification>();
75 private static String getCategoryFromNotificationId(int id) {
76 return XWALK_INTENT_CATEGORY_NOTIFICATION_PREFIX + id;
80 public void setBridge(XWalkContentsClientBridge bridge) {
85 public void shutdown() {
86 if (!mExistNotificationIds.isEmpty()) {
93 public boolean maybeHandleIntent(Intent intent) {
94 if (intent.getAction() == null) return false;
95 int notificationId = intent.getIntExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, -1);
96 if (notificationId <= 0) return false;
97 if (intent.getAction().equals(
98 mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX)) {
99 onNotificationClose(notificationId, true);
101 } else if (intent.getAction().equals(
102 mView.getActivity().getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX)) {
103 onNotificationClick(notificationId);
109 public Bitmap getNotificationIcon(Bitmap icon) {
110 if (icon == null) return null;
111 int originalWidth = icon.getWidth();
112 int originalHeight = icon.getHeight();
113 if (originalWidth == 0 || originalHeight == 0) {
117 int targetWidth = mContext.getResources().getDimensionPixelSize(
118 android.R.dimen.notification_large_icon_width);
119 int targetHeight = mContext.getResources().getDimensionPixelSize(
120 android.R.dimen.notification_large_icon_height);
122 if (originalWidth > targetWidth && originalHeight > targetHeight) {
123 if (originalWidth * targetHeight > originalHeight * targetWidth) {
124 targetHeight = originalHeight * targetWidth / originalWidth;
126 targetWidth = originalWidth * targetHeight / originalHeight;
130 return Bitmap.createScaledBitmap(icon, targetWidth, targetHeight, true);
134 @SuppressWarnings("deprecation")
135 public void showNotification(String title, String message, String replaceId,
136 Bitmap icon, int notificationId) {
137 Notification.Builder builder;
139 if (!replaceId.isEmpty() && mExistReplaceIds.containsKey(replaceId)) {
140 WebNotification webNotification = mExistReplaceIds.get(replaceId);
141 notificationId = webNotification.mNotificationId;
142 builder = webNotification.mBuilder;
143 builder.setNumber(++webNotification.mMessageNum);
145 builder = new Notification.Builder(mContext.getApplicationContext())
146 .setAutoCancel(true);
148 WebNotification webNotification = new WebNotification();
149 webNotification.mNotificationId = notificationId;
150 webNotification.mReplaceId = replaceId;
151 webNotification.mBuilder = builder;
153 mExistNotificationIds.put(notificationId, webNotification);
154 if (!replaceId.isEmpty()) {
155 mExistReplaceIds.put(replaceId, webNotification);
159 builder.setContentTitle(title);
160 builder.setContentText(message);
162 int iconRes = mContext.getApplicationInfo().icon;
164 iconRes = android.R.drawable.sym_def_app_icon;
166 builder.setSmallIcon(iconRes);
167 Bitmap bigIcon = getNotificationIcon(icon);
168 if (bigIcon != null) builder.setLargeIcon(bigIcon);
170 Context activity = mView.getActivity();
171 String category = getCategoryFromNotificationId(notificationId);
173 Intent clickIntent = new Intent(activity, activity.getClass())
174 .setAction(activity.getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX)
175 .putExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, notificationId)
176 .setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | Intent.FLAG_ACTIVITY_SINGLE_TOP)
177 .addCategory(category);
179 Intent closeIntent = new Intent(activity.getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX)
180 .putExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, notificationId)
181 .addCategory(category);
183 builder.setContentIntent(PendingIntent.getActivity(
184 activity, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT));
185 builder.setDeleteIntent(PendingIntent.getBroadcast(
186 activity, 0, closeIntent, PendingIntent.FLAG_UPDATE_CURRENT));
188 doShowNotification(notificationId,
189 VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN ? builder.build() : builder.getNotification());
190 notificationChanged();
191 onNotificationShown(notificationId);
195 public void cancelNotification(int notificationId) {
196 mNotificationManager.cancel(notificationId);
197 onNotificationClose(notificationId, false);
200 public void doShowNotification(int notificationId, Notification notification) {
201 mNotificationManager.notify(notificationId, notification);
204 public void onNotificationShown(int notificationId) {
205 WebNotification webNotification = mExistNotificationIds.get(notificationId);
206 if (webNotification == null) {
210 if (mBridge != null) {
211 mBridge.notificationDisplayed(notificationId);
215 public void onNotificationClick(int notificationId) {
216 WebNotification webNotification = mExistNotificationIds.get(notificationId);
217 if (webNotification == null) {
221 mExistNotificationIds.remove(notificationId);
222 mExistReplaceIds.remove(webNotification.mReplaceId);
224 notificationChanged();
225 if (mBridge != null) {
226 mBridge.notificationClicked(notificationId);
230 public void onNotificationClose(
231 int notificationId, boolean byUser) {
232 WebNotification webNotification = mExistNotificationIds.get(notificationId);
233 if (webNotification == null) {
237 mExistNotificationIds.remove(notificationId);
238 mExistReplaceIds.remove(webNotification.mReplaceId);
240 notificationChanged();
241 if (mBridge != null) {
242 mBridge.notificationClosed(notificationId, byUser);
246 private void notificationChanged() {
247 if (mExistNotificationIds.isEmpty()) {
248 Log.i(TAG, "notifications are all cleared," +
249 "unregister broadcast receiver for close pending intent");
250 unregisterReceiver();
256 private void registerReceiver() {
257 IntentFilter filter = new IntentFilter(
258 mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX);
260 for(Integer id : mExistNotificationIds.keySet()) {
261 filter.addCategory(getCategoryFromNotificationId(id));
265 mView.getActivity().registerReceiver(mNotificationCloseReceiver, filter);
266 } catch (AndroidRuntimeException e) {
267 //FIXME(wang16): The exception will happen when there are multiple xwalkviews in one activity.
268 // Remove it after notification service supports multi-views.
269 Log.w(TAG, e.getLocalizedMessage());
273 private void unregisterReceiver() {
274 mView.getActivity().unregisterReceiver(mNotificationCloseReceiver);