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.client;
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;
26 public class XWalkDefaultNotificationService implements XWalkNotificationService {
27 private static final String TAG = "XWalkDefaultNotificationService";
29 private static final String XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX = ".notification.click";
30 private static final String XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX = ".notification.close";
31 private static final String XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID = "xwalk.NOTIFICATION_ID";
32 private static final String XWALK_INTENT_EXTRA_KEY_PROCESS_ID = "xwalk.PROCESS_ID";
33 private static final String XWALK_INTENT_EXTRA_KEY_ROUTE_ID = "xwalk.ROUTE_ID";
34 private static final String XWALK_INTENT_CATEGORY_NOTIFICATION_PREFIX = "notification_";
36 private Context mContext;
37 private XWalkContentsClientBridge mBridge;
38 private XWalkView mView;
39 private NotificationManager mNotificationManager;
40 private BroadcastReceiver mNotificationCloseReceiver;
41 private IntentFilter mNotificationCloseIntentFilter;
42 private HashMap<Integer, Notification.Builder> mExistNotificationIds;
44 public XWalkDefaultNotificationService(Context context, XWalkView view) {
47 mNotificationManager =
48 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
49 // Cancel all exist notifications at startup time. To avoid receiving legacy pendingIntents.
50 mNotificationManager.cancelAll();
51 mExistNotificationIds = new HashMap<Integer, Notification.Builder>();
55 public void setBridge(XWalkContentsClientBridge bridge) {
59 private static String getCategoryFromNotificationId(int id) {
60 return XWALK_INTENT_CATEGORY_NOTIFICATION_PREFIX + id;
63 private void notificationChanged() {
65 if (mExistNotificationIds.isEmpty()) {
66 Log.i(TAG, "notifications are all cleared," +
67 "unregister broadcast receiver for close pending intent");
73 private void registerReceiver() {
74 mNotificationCloseReceiver = new BroadcastReceiver() {
76 public void onReceive(Context context, Intent intent) {
77 mView.onNewIntent(intent);
80 mNotificationCloseIntentFilter = new IntentFilter(
81 mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX);
82 for(Integer id : mExistNotificationIds.keySet()) {
83 mNotificationCloseIntentFilter.addCategory(getCategoryFromNotificationId(id));
86 mView.getActivity().registerReceiver(mNotificationCloseReceiver, mNotificationCloseIntentFilter);
87 } catch (AndroidRuntimeException e) {
88 //FIXME(wang16): The exception will happen when there are multiple xwalkviews in one activity.
89 // Remove it after notification service supports multi-views.
90 mNotificationCloseReceiver = null;
91 Log.w(TAG, e.getLocalizedMessage());
95 private void unregisterReceiver() {
96 if (mNotificationCloseReceiver != null) {
97 mView.getActivity().unregisterReceiver(mNotificationCloseReceiver);
98 mNotificationCloseReceiver = null;
103 public void shutdown() {
104 unregisterReceiver();
109 public boolean maybeHandleIntent(Intent intent) {
110 if (intent.getAction() == null) return false;
111 int notificationId = intent.getIntExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, -1);
112 int processId = intent.getIntExtra(XWALK_INTENT_EXTRA_KEY_PROCESS_ID, -1);
113 int routeId = intent.getIntExtra(XWALK_INTENT_EXTRA_KEY_ROUTE_ID, -1);
114 if (notificationId < 0) return false;
115 if (intent.getAction().equals(
116 mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX)) {
117 onNotificationClose(notificationId, true, processId, routeId);
119 } else if (intent.getAction().equals(
120 mView.getActivity().getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX)) {
121 onNotificationClick(notificationId, processId, routeId);
128 @SuppressWarnings("deprecation")
129 public void updateNotificationIcon(int notificationId, Bitmap icon) {
130 Notification.Builder builder = mExistNotificationIds.get(notificationId);
131 if (builder != null) {
132 int originalWidth = icon.getWidth();
133 int originalHeight = icon.getHeight();
134 if (originalWidth == 0 || originalHeight == 0)
136 int targetWidth = mContext.getResources().getDimensionPixelSize(
137 android.R.dimen.notification_large_icon_width);
138 int targetHeight = mContext.getResources().getDimensionPixelSize(
139 android.R.dimen.notification_large_icon_height);
140 if (originalWidth > targetWidth && originalHeight > targetHeight) {
141 if (originalWidth * targetHeight > originalHeight * targetWidth) {
142 targetHeight = originalHeight * targetWidth / originalWidth;
144 targetWidth = originalWidth * targetHeight / originalHeight;
147 builder.setLargeIcon(
148 Bitmap.createScaledBitmap(icon, targetWidth, targetHeight, true));
149 Notification notification;
150 if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
151 notification = builder.build();
153 notification = builder.getNotification();
155 doShowNotification(notificationId, notification);
156 mExistNotificationIds.put(notificationId, builder);
161 @SuppressWarnings("deprecation")
162 public void showNotification(String title, String message,
163 int notificationId, int processId, int routeId) {
164 Context activity = mView.getActivity();
165 String category = getCategoryFromNotificationId(notificationId);
166 Intent clickIntent = new Intent(activity, activity.getClass());
167 clickIntent.setAction(activity.getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX);
168 clickIntent.putExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, notificationId);
169 clickIntent.putExtra(XWALK_INTENT_EXTRA_KEY_PROCESS_ID, processId);
170 clickIntent.putExtra(XWALK_INTENT_EXTRA_KEY_ROUTE_ID, routeId);
171 clickIntent.setFlags(
172 Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | Intent.FLAG_ACTIVITY_SINGLE_TOP);
173 clickIntent.addCategory(category);
174 PendingIntent pendingClickIntent = PendingIntent.getActivity(activity,
175 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
177 new Intent(activity.getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX);
178 closeIntent.putExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, notificationId);
179 closeIntent.putExtra(XWALK_INTENT_EXTRA_KEY_PROCESS_ID, processId);
180 closeIntent.putExtra(XWALK_INTENT_EXTRA_KEY_ROUTE_ID, routeId);
181 closeIntent.addCategory(category);
182 PendingIntent pendingCloseIntent = PendingIntent.getBroadcast(activity,
183 0, closeIntent, PendingIntent.FLAG_UPDATE_CURRENT);
184 Notification.Builder builder = new Notification.Builder(mContext.getApplicationContext())
185 .setContentIntent(pendingClickIntent)
186 .setDeleteIntent(pendingCloseIntent);
187 int iconRes = mContext.getApplicationInfo().icon;
188 if (iconRes == 0) iconRes = android.R.drawable.sym_def_app_icon;
189 builder = builder.setContentText(message)
190 .setContentTitle(title)
191 .setSmallIcon(iconRes)
192 .setAutoCancel(true);
193 Notification notification;
194 if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
195 notification = builder.build();
197 notification = builder.getNotification();
199 doShowNotification(notificationId, notification);
200 mExistNotificationIds.put(notificationId, builder);
201 notificationChanged();
202 onNotificationShown(notificationId, processId, routeId);
206 public void cancelNotification(int notificationId, int processId, int routeId) {
207 mNotificationManager.cancel(notificationId);
208 onNotificationClose(notificationId, false, processId, routeId);
211 public void doShowNotification(int id, Notification notification) {
212 mNotificationManager.notify(id, notification);
215 public void onNotificationShown(int notificationId, int processId, int routeId) {
216 if (mExistNotificationIds.containsKey(notificationId) && mBridge != null) {
217 mBridge.notificationDisplayed(notificationId, processId, routeId);
221 public void onNotificationClick(int notificationId, int processId, int routeId) {
222 if (mExistNotificationIds.containsKey(notificationId)) {
223 mExistNotificationIds.remove(notificationId);
224 notificationChanged();
225 if (mBridge != null) {
226 mBridge.notificationClicked(notificationId, processId, routeId);
231 public void onNotificationClose(
232 int notificationId, boolean byUser, int processId, int routeId) {
233 if (mExistNotificationIds.containsKey(notificationId)) {
234 mExistNotificationIds.remove(notificationId);
235 notificationChanged();
236 if (mBridge != null) {
237 mBridge.notificationClosed(notificationId, byUser, processId, routeId);