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;
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.XWalkContentsClientBridge;
23 import org.xwalk.core.XWalkNotificationService;
24 import org.xwalk.core.XWalkView;
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_PROCESS_ID = "xwalk.PROCESS_ID";
36 private static final String XWALK_INTENT_EXTRA_KEY_ROUTE_ID = "xwalk.ROUTE_ID";
37 private static final String XWALK_INTENT_CATEGORY_NOTIFICATION_PREFIX = "notification_";
39 private Context mContext;
40 private XWalkContentsClientBridge mBridge;
41 private XWalkView mView;
42 private NotificationManager mNotificationManager;
43 private BroadcastReceiver mNotificationCloseReceiver;
44 private IntentFilter mNotificationCloseIntentFilter;
45 private HashMap<Integer, Notification.Builder> mExistNotificationIds;
47 public XWalkNotificationServiceImpl(Context context, XWalkView view) {
50 mNotificationManager =
51 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
52 // Cancel all exist notifications at startup time. To avoid receiving legacy pendingIntents.
53 mNotificationManager.cancelAll();
54 mExistNotificationIds = new HashMap<Integer, Notification.Builder>();
58 public void setBridge(XWalkContentsClientBridge bridge) {
62 private static String getCategoryFromNotificationId(int id) {
63 return XWALK_INTENT_CATEGORY_NOTIFICATION_PREFIX + id;
66 private void notificationChanged() {
68 if (mExistNotificationIds.isEmpty()) {
69 Log.i(TAG, "notifications are all cleared," +
70 "unregister broadcast receiver for close pending intent");
76 private void registerReceiver() {
77 mNotificationCloseReceiver = new BroadcastReceiver() {
79 public void onReceive(Context context, Intent intent) {
80 mView.onNewIntent(intent);
83 mNotificationCloseIntentFilter = new IntentFilter(
84 mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX);
85 for(Integer id : mExistNotificationIds.keySet()) {
86 mNotificationCloseIntentFilter.addCategory(getCategoryFromNotificationId(id));
89 mView.getActivity().registerReceiver(mNotificationCloseReceiver, mNotificationCloseIntentFilter);
90 } catch (AndroidRuntimeException e) {
91 //FIXME(wang16): The exception will happen when there are multiple xwalkviews in one activity.
92 // Remove it after notification service supports multi-views.
93 mNotificationCloseReceiver = null;
94 Log.w(TAG, e.getLocalizedMessage());
98 private void unregisterReceiver() {
99 if (mNotificationCloseReceiver != null) {
100 mView.getActivity().unregisterReceiver(mNotificationCloseReceiver);
101 mNotificationCloseReceiver = null;
106 public void shutdown() {
107 unregisterReceiver();
112 public boolean maybeHandleIntent(Intent intent) {
113 if (intent.getAction() == null) return false;
114 int notificationId = intent.getIntExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, -1);
115 int processId = intent.getIntExtra(XWALK_INTENT_EXTRA_KEY_PROCESS_ID, -1);
116 int routeId = intent.getIntExtra(XWALK_INTENT_EXTRA_KEY_ROUTE_ID, -1);
117 if (notificationId < 0) return false;
118 if (intent.getAction().equals(
119 mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX)) {
120 onNotificationClose(notificationId, true, processId, routeId);
122 } else if (intent.getAction().equals(
123 mView.getActivity().getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX)) {
124 onNotificationClick(notificationId, processId, routeId);
131 @SuppressWarnings("deprecation")
132 public void updateNotificationIcon(int notificationId, Bitmap icon) {
133 Notification.Builder builder = mExistNotificationIds.get(notificationId);
134 if (builder != null) {
135 int originalWidth = icon.getWidth();
136 int originalHeight = icon.getHeight();
137 if (originalWidth == 0 || originalHeight == 0)
139 int targetWidth = mContext.getResources().getDimensionPixelSize(
140 android.R.dimen.notification_large_icon_width);
141 int targetHeight = mContext.getResources().getDimensionPixelSize(
142 android.R.dimen.notification_large_icon_height);
143 if (originalWidth > targetWidth && originalHeight > targetHeight) {
144 if (originalWidth * targetHeight > originalHeight * targetWidth) {
145 targetHeight = originalHeight * targetWidth / originalWidth;
147 targetWidth = originalWidth * targetHeight / originalHeight;
150 builder.setLargeIcon(
151 Bitmap.createScaledBitmap(icon, targetWidth, targetHeight, true));
152 Notification notification;
153 if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
154 notification = builder.build();
156 notification = builder.getNotification();
158 doShowNotification(notificationId, notification);
159 mExistNotificationIds.put(notificationId, builder);
164 @SuppressWarnings("deprecation")
165 public void showNotification(String title, String message,
166 int notificationId, int processId, int routeId) {
167 Context activity = mView.getActivity();
168 String category = getCategoryFromNotificationId(notificationId);
169 Intent clickIntent = new Intent(activity, activity.getClass());
170 clickIntent.setAction(activity.getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX);
171 clickIntent.putExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, notificationId);
172 clickIntent.putExtra(XWALK_INTENT_EXTRA_KEY_PROCESS_ID, processId);
173 clickIntent.putExtra(XWALK_INTENT_EXTRA_KEY_ROUTE_ID, routeId);
174 clickIntent.setFlags(
175 Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | Intent.FLAG_ACTIVITY_SINGLE_TOP);
176 clickIntent.addCategory(category);
177 PendingIntent pendingClickIntent = PendingIntent.getActivity(activity,
178 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
180 new Intent(activity.getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX);
181 closeIntent.putExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, notificationId);
182 closeIntent.putExtra(XWALK_INTENT_EXTRA_KEY_PROCESS_ID, processId);
183 closeIntent.putExtra(XWALK_INTENT_EXTRA_KEY_ROUTE_ID, routeId);
184 closeIntent.addCategory(category);
185 PendingIntent pendingCloseIntent = PendingIntent.getBroadcast(activity,
186 0, closeIntent, PendingIntent.FLAG_UPDATE_CURRENT);
187 Notification.Builder builder = new Notification.Builder(mContext.getApplicationContext())
188 .setContentIntent(pendingClickIntent)
189 .setDeleteIntent(pendingCloseIntent);
190 int iconRes = mContext.getApplicationInfo().icon;
191 if (iconRes == 0) iconRes = android.R.drawable.sym_def_app_icon;
192 builder = builder.setContentText(message)
193 .setContentTitle(title)
194 .setSmallIcon(iconRes)
195 .setAutoCancel(true);
196 Notification notification;
197 if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
198 notification = builder.build();
200 notification = builder.getNotification();
202 doShowNotification(notificationId, notification);
203 mExistNotificationIds.put(notificationId, builder);
204 notificationChanged();
205 onNotificationShown(notificationId, processId, routeId);
209 public void cancelNotification(int notificationId, int processId, int routeId) {
210 mNotificationManager.cancel(notificationId);
211 onNotificationClose(notificationId, false, processId, routeId);
214 public void doShowNotification(int id, Notification notification) {
215 mNotificationManager.notify(id, notification);
218 public void onNotificationShown(int notificationId, int processId, int routeId) {
219 if (mExistNotificationIds.containsKey(notificationId) && mBridge != null) {
220 mBridge.notificationDisplayed(notificationId, processId, routeId);
224 public void onNotificationClick(int notificationId, int processId, int routeId) {
225 if (mExistNotificationIds.containsKey(notificationId)) {
226 mExistNotificationIds.remove(notificationId);
227 notificationChanged();
228 if (mBridge != null) {
229 mBridge.notificationClicked(notificationId, processId, routeId);
234 public void onNotificationClose(
235 int notificationId, boolean byUser, int processId, int routeId) {
236 if (mExistNotificationIds.containsKey(notificationId)) {
237 mExistNotificationIds.remove(notificationId);
238 notificationChanged();
239 if (mBridge != null) {
240 mBridge.notificationClosed(notificationId, byUser, processId, routeId);