Upstream version 9.37.195.0
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / core_internal / src / org / xwalk / core / internal / XWalkNotificationServiceImpl.java
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.
4
5 package org.xwalk.core.internal;
6
7 import java.util.HashMap;
8
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;
21
22 import org.xwalk.core.internal.XWalkContentsClientBridge;
23 import org.xwalk.core.internal.XWalkNotificationService;
24 import org.xwalk.core.internal.XWalkViewInternal;
25
26 /**
27  * @hide
28  */
29 public class XWalkNotificationServiceImpl implements XWalkNotificationService {
30     private static final String TAG = "XWalkNotificationServiceImpl";
31
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_";
37
38     private class WebNotification {
39         WebNotification() {
40             mMessageNum = 1;
41         }
42
43         public Integer mNotificationId;
44         public String  mReplaceId;
45         public Notification.Builder mBuilder;
46         public Integer mMessageNum;
47     }
48
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;
56
57     public XWalkNotificationServiceImpl(Context context, XWalkViewInternal view) {
58         mContext = context;
59         mView = 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();
64
65         mNotificationCloseReceiver = new BroadcastReceiver() {
66             @Override
67             public void onReceive(Context context, Intent intent) {
68                 mView.onNewIntent(intent);
69             }
70         };
71
72         mExistNotificationIds = new HashMap<Integer, WebNotification>();
73         mExistReplaceIds      = new HashMap<String, WebNotification>();
74     }
75
76     private static String getCategoryFromNotificationId(int id) {
77         return XWALK_INTENT_CATEGORY_NOTIFICATION_PREFIX + id;
78     }
79
80     @Override
81     public void setBridge(XWalkContentsClientBridge bridge) {
82         mBridge = bridge;
83     }
84
85     @Override
86     public void shutdown() {
87         if (!mExistNotificationIds.isEmpty()) {
88             unregisterReceiver();
89         }
90         mBridge = null;
91     }
92
93     @Override
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);
102             return true;
103         } else if (intent.getAction().equals(
104                 mView.getActivity().getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX)) {
105             onNotificationClick(notificationId, delegate);
106             return true;
107         }
108         return false;
109     }
110
111     @Override
112     @SuppressWarnings("deprecation")
113     public void updateNotificationIcon(int notificationId, Bitmap icon) {
114         WebNotification webNotification = mExistNotificationIds.get(notificationId);
115         if (webNotification == null) {
116             return;
117         }
118
119         int originalWidth  = icon.getWidth();
120         int originalHeight = icon.getHeight();
121         if (originalWidth == 0 || originalHeight == 0) {
122             return;
123         }
124
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);
129
130         if (originalWidth > targetWidth && originalHeight > targetHeight) {
131             if (originalWidth * targetHeight > originalHeight * targetWidth) {
132                 targetHeight = originalHeight * targetWidth / originalWidth;
133             } else {
134                 targetWidth = originalWidth * targetHeight / originalHeight;
135             }
136         }
137
138         Notification.Builder builder = webNotification.mBuilder;
139         builder.setLargeIcon(Bitmap.createScaledBitmap(icon, targetWidth, targetHeight, true));
140
141         doShowNotification(notificationId, 
142                 VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN ? builder.build() : builder.getNotification());
143     }
144
145     @Override
146     @SuppressWarnings("deprecation")
147     public void showNotification(String title, String message, String replaceId,
148             int notificationId, long delegate) {
149         Notification.Builder builder;
150
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);
156         } else {
157             builder = new Notification.Builder(mContext.getApplicationContext())
158                     .setAutoCancel(true);
159
160             WebNotification webNotification = new WebNotification();
161             webNotification.mNotificationId = notificationId;
162             webNotification.mReplaceId = replaceId;
163             webNotification.mBuilder = builder;
164
165             mExistNotificationIds.put(notificationId, webNotification);
166             if (!replaceId.isEmpty()) {
167                 mExistReplaceIds.put(replaceId, webNotification);
168             }
169         }
170
171         builder.setContentTitle(title);
172         builder.setContentText(message);
173
174         int iconRes = mContext.getApplicationInfo().icon;
175         if (iconRes == 0) {
176             iconRes = android.R.drawable.sym_def_app_icon;
177         }
178         builder.setSmallIcon(iconRes);
179
180         Context activity = mView.getActivity();
181         String category = getCategoryFromNotificationId(notificationId);
182
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);
189
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);
194
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));
199
200         doShowNotification(notificationId, 
201                 VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN ? builder.build() : builder.getNotification());
202         notificationChanged();
203         onNotificationShown(notificationId, delegate);
204     }
205
206     @Override
207     public void cancelNotification(int notificationId, long delegate) {
208         mNotificationManager.cancel(notificationId);
209         onNotificationClose(notificationId, false, delegate);
210     }
211
212     public void doShowNotification(int notificationId, Notification notification) {
213         mNotificationManager.notify(notificationId, notification);
214     }
215
216     public void onNotificationShown(int notificationId, long delegate) {
217         WebNotification webNotification = mExistNotificationIds.get(notificationId);
218         if (webNotification == null) {
219             return;
220         }
221
222         if (mBridge != null) {
223             mBridge.notificationDisplayed(delegate);
224         }
225     }
226
227     public void onNotificationClick(int notificationId, long delegate) {
228         WebNotification webNotification = mExistNotificationIds.get(notificationId);
229         if (webNotification == null) {
230             return;
231         }
232
233         mExistNotificationIds.remove(notificationId);
234         mExistReplaceIds.remove(webNotification.mReplaceId);
235
236         notificationChanged();
237         if (mBridge != null) {
238             mBridge.notificationClicked(notificationId, delegate);
239         }
240     }
241
242     public void onNotificationClose(
243             int notificationId, boolean byUser, long delegate) {
244         WebNotification webNotification = mExistNotificationIds.get(notificationId);
245         if (webNotification == null) {
246             return;
247         }
248
249         mExistNotificationIds.remove(notificationId);
250         mExistReplaceIds.remove(webNotification.mReplaceId);
251
252         notificationChanged();
253         if (mBridge != null) {
254             mBridge.notificationClosed(notificationId, byUser, delegate);
255         }
256     }
257
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();
263         } else {
264             registerReceiver();
265         }
266     }
267
268     private void registerReceiver() {
269         IntentFilter filter = new IntentFilter(
270                 mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX);
271
272         for(Integer id : mExistNotificationIds.keySet()) {
273             filter.addCategory(getCategoryFromNotificationId(id));
274         }
275         
276         try {
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());
282         }
283     }
284
285     private void unregisterReceiver() {
286         mView.getActivity().unregisterReceiver(mNotificationCloseReceiver);
287     }
288 }