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 Context mContext;
39 private XWalkContentsClientBridge mBridge;
40 private XWalkViewInternal mView;
41 private NotificationManager mNotificationManager;
42 private BroadcastReceiver mNotificationCloseReceiver;
43 private IntentFilter mNotificationCloseIntentFilter;
44 private HashMap<Integer, Notification.Builder> mExistNotificationIds;
46 public XWalkNotificationServiceImpl(Context context, XWalkViewInternal view) {
49 mNotificationManager =
50 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
51 // Cancel all exist notifications at startup time. To avoid receiving legacy pendingIntents.
52 mNotificationManager.cancelAll();
53 mExistNotificationIds = new HashMap<Integer, Notification.Builder>();
57 public void setBridge(XWalkContentsClientBridge bridge) {
61 private static String getCategoryFromNotificationId(int id) {
62 return XWALK_INTENT_CATEGORY_NOTIFICATION_PREFIX + id;
65 private void notificationChanged() {
67 if (mExistNotificationIds.isEmpty()) {
68 Log.i(TAG, "notifications are all cleared," +
69 "unregister broadcast receiver for close pending intent");
75 private void registerReceiver() {
76 mNotificationCloseReceiver = new BroadcastReceiver() {
78 public void onReceive(Context context, Intent intent) {
79 mView.onNewIntent(intent);
82 mNotificationCloseIntentFilter = new IntentFilter(
83 mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX);
84 for(Integer id : mExistNotificationIds.keySet()) {
85 mNotificationCloseIntentFilter.addCategory(getCategoryFromNotificationId(id));
88 mView.getActivity().registerReceiver(mNotificationCloseReceiver, mNotificationCloseIntentFilter);
89 } catch (AndroidRuntimeException e) {
90 //FIXME(wang16): The exception will happen when there are multiple xwalkviews in one activity.
91 // Remove it after notification service supports multi-views.
92 mNotificationCloseReceiver = null;
93 Log.w(TAG, e.getLocalizedMessage());
97 private void unregisterReceiver() {
98 if (mNotificationCloseReceiver != null) {
99 mView.getActivity().unregisterReceiver(mNotificationCloseReceiver);
100 mNotificationCloseReceiver = null;
105 public void shutdown() {
106 unregisterReceiver();
111 public boolean maybeHandleIntent(Intent intent) {
112 if (intent.getAction() == null) return false;
113 int notificationId = intent.getIntExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, -1);
114 long delegate = intent.getLongExtra(XWALK_INTENT_EXTRA_KEY_DELEGATE, -1);
115 if (notificationId <= 0) return false;
116 if (intent.getAction().equals(
117 mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX)) {
118 onNotificationClose(notificationId, true, delegate);
120 } else if (intent.getAction().equals(
121 mView.getActivity().getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX)) {
122 onNotificationClick(notificationId, delegate);
129 @SuppressWarnings("deprecation")
130 public void updateNotificationIcon(int notificationId, Bitmap icon) {
131 Notification.Builder builder = mExistNotificationIds.get(notificationId);
132 if (builder != null) {
133 int originalWidth = icon.getWidth();
134 int originalHeight = icon.getHeight();
135 if (originalWidth == 0 || originalHeight == 0)
137 int targetWidth = mContext.getResources().getDimensionPixelSize(
138 android.R.dimen.notification_large_icon_width);
139 int targetHeight = mContext.getResources().getDimensionPixelSize(
140 android.R.dimen.notification_large_icon_height);
141 if (originalWidth > targetWidth && originalHeight > targetHeight) {
142 if (originalWidth * targetHeight > originalHeight * targetWidth) {
143 targetHeight = originalHeight * targetWidth / originalWidth;
145 targetWidth = originalWidth * targetHeight / originalHeight;
148 builder.setLargeIcon(
149 Bitmap.createScaledBitmap(icon, targetWidth, targetHeight, true));
150 Notification notification;
151 if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
152 notification = builder.build();
154 notification = builder.getNotification();
156 doShowNotification(notificationId, notification);
157 mExistNotificationIds.put(notificationId, builder);
162 @SuppressWarnings("deprecation")
163 public void showNotification(String title, String message,
164 int notificationId, long delegate) {
165 Context activity = mView.getActivity();
166 String category = getCategoryFromNotificationId(notificationId);
167 Intent clickIntent = new Intent(activity, activity.getClass());
168 clickIntent.setAction(activity.getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX);
169 clickIntent.putExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, notificationId);
170 clickIntent.putExtra(XWALK_INTENT_EXTRA_KEY_DELEGATE, delegate);
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_DELEGATE, delegate);
180 closeIntent.addCategory(category);
181 PendingIntent pendingCloseIntent = PendingIntent.getBroadcast(activity,
182 0, closeIntent, PendingIntent.FLAG_UPDATE_CURRENT);
183 Notification.Builder builder = new Notification.Builder(mContext.getApplicationContext())
184 .setContentIntent(pendingClickIntent)
185 .setDeleteIntent(pendingCloseIntent);
186 int iconRes = mContext.getApplicationInfo().icon;
187 if (iconRes == 0) iconRes = android.R.drawable.sym_def_app_icon;
188 builder = builder.setContentText(message)
189 .setContentTitle(title)
190 .setSmallIcon(iconRes)
191 .setAutoCancel(true);
192 Notification notification;
193 if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
194 notification = builder.build();
196 notification = builder.getNotification();
198 doShowNotification(notificationId, notification);
199 mExistNotificationIds.put(notificationId, builder);
200 notificationChanged();
201 onNotificationShown(notificationId, delegate);
205 public void cancelNotification(int notificationId, long delegate) {
206 mNotificationManager.cancel(notificationId);
207 onNotificationClose(notificationId, false, delegate);
210 public void doShowNotification(int id, Notification notification) {
211 mNotificationManager.notify(id, notification);
214 public void onNotificationShown(int notificationId, long delegate) {
215 if (mExistNotificationIds.containsKey(notificationId) && mBridge != null) {
216 mBridge.notificationDisplayed(delegate);
220 public void onNotificationClick(int notificationId, long delegate) {
221 if (mExistNotificationIds.containsKey(notificationId)) {
222 mExistNotificationIds.remove(notificationId);
223 notificationChanged();
224 if (mBridge != null) {
225 mBridge.notificationClicked(notificationId, delegate);
230 public void onNotificationClose(
231 int notificationId, boolean byUser, long delegate) {
232 if (mExistNotificationIds.containsKey(notificationId)) {
233 mExistNotificationIds.remove(notificationId);
234 notificationChanged();
235 if (mBridge != null) {
236 mBridge.notificationClosed(notificationId, byUser, delegate);