Upstream version 11.40.277.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_CATEGORY_NOTIFICATION_PREFIX = "notification_";
36
37     private class WebNotification {
38         WebNotification() {
39             mMessageNum = 1;
40         }
41
42         public Integer mNotificationId;
43         public String  mReplaceId;
44         public Notification.Builder mBuilder;
45         public Integer mMessageNum;
46     }
47
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;
55
56     public XWalkNotificationServiceImpl(Context context, XWalkViewInternal view) {
57         mContext = context;
58         mView = 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();
63
64         mNotificationCloseReceiver = new BroadcastReceiver() {
65             @Override
66             public void onReceive(Context context, Intent intent) {
67                 mView.onNewIntent(intent);
68             }
69         };
70
71         mExistNotificationIds = new HashMap<Integer, WebNotification>();
72         mExistReplaceIds      = new HashMap<String, WebNotification>();
73     }
74
75     private static String getCategoryFromNotificationId(int id) {
76         return XWALK_INTENT_CATEGORY_NOTIFICATION_PREFIX + id;
77     }
78
79     @Override
80     public void setBridge(XWalkContentsClientBridge bridge) {
81         mBridge = bridge;
82     }
83
84     @Override
85     public void shutdown() {
86         if (!mExistNotificationIds.isEmpty()) {
87             unregisterReceiver();
88         }
89         mBridge = null;
90     }
91
92     @Override
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);
100             return true;
101         } else if (intent.getAction().equals(
102                 mView.getActivity().getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX)) {
103             onNotificationClick(notificationId);
104             return true;
105         }
106         return false;
107     }
108
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) {
114             return icon;
115         }
116
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);
121
122         if (originalWidth > targetWidth && originalHeight > targetHeight) {
123             if (originalWidth * targetHeight > originalHeight * targetWidth) {
124                 targetHeight = originalHeight * targetWidth / originalWidth;
125             } else {
126                 targetWidth = originalWidth * targetHeight / originalHeight;
127             }
128         }
129
130         return Bitmap.createScaledBitmap(icon, targetWidth, targetHeight, true);
131     }
132
133     @Override
134     @SuppressWarnings("deprecation")
135     public void showNotification(String title, String message, String replaceId,
136             Bitmap icon, int notificationId) {
137         Notification.Builder builder;
138
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);
144         } else {
145             builder = new Notification.Builder(mContext.getApplicationContext())
146                     .setAutoCancel(true);
147
148             WebNotification webNotification = new WebNotification();
149             webNotification.mNotificationId = notificationId;
150             webNotification.mReplaceId = replaceId;
151             webNotification.mBuilder = builder;
152
153             mExistNotificationIds.put(notificationId, webNotification);
154             if (!replaceId.isEmpty()) {
155                 mExistReplaceIds.put(replaceId, webNotification);
156             }
157         }
158
159         builder.setContentTitle(title);
160         builder.setContentText(message);
161
162         int iconRes = mContext.getApplicationInfo().icon;
163         if (iconRes == 0) {
164             iconRes = android.R.drawable.sym_def_app_icon;
165         }
166         builder.setSmallIcon(iconRes);
167         Bitmap bigIcon = getNotificationIcon(icon);
168         if (bigIcon != null) builder.setLargeIcon(bigIcon);
169
170         Context activity = mView.getActivity();
171         String category = getCategoryFromNotificationId(notificationId);
172
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);
178
179         Intent closeIntent = new Intent(activity.getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX)
180                 .putExtra(XWALK_INTENT_EXTRA_KEY_NOTIFICATION_ID, notificationId)
181                 .addCategory(category);
182
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));
187
188         doShowNotification(notificationId, 
189                 VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN ? builder.build() : builder.getNotification());
190         notificationChanged();
191         onNotificationShown(notificationId);
192     }
193
194     @Override
195     public void cancelNotification(int notificationId) {
196         mNotificationManager.cancel(notificationId);
197         onNotificationClose(notificationId, false);
198     }
199
200     public void doShowNotification(int notificationId, Notification notification) {
201         mNotificationManager.notify(notificationId, notification);
202     }
203
204     public void onNotificationShown(int notificationId) {
205         WebNotification webNotification = mExistNotificationIds.get(notificationId);
206         if (webNotification == null) {
207             return;
208         }
209
210         if (mBridge != null) {
211             mBridge.notificationDisplayed(notificationId);
212         }
213     }
214
215     public void onNotificationClick(int notificationId) {
216         WebNotification webNotification = mExistNotificationIds.get(notificationId);
217         if (webNotification == null) {
218             return;
219         }
220
221         mExistNotificationIds.remove(notificationId);
222         mExistReplaceIds.remove(webNotification.mReplaceId);
223
224         notificationChanged();
225         if (mBridge != null) {
226             mBridge.notificationClicked(notificationId);
227         }
228     }
229
230     public void onNotificationClose(
231             int notificationId, boolean byUser) {
232         WebNotification webNotification = mExistNotificationIds.get(notificationId);
233         if (webNotification == null) {
234             return;
235         }
236
237         mExistNotificationIds.remove(notificationId);
238         mExistReplaceIds.remove(webNotification.mReplaceId);
239
240         notificationChanged();
241         if (mBridge != null) {
242             mBridge.notificationClosed(notificationId, byUser);
243         }
244     }
245
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();
251         } else {
252             registerReceiver();
253         }
254     }
255
256     private void registerReceiver() {
257         IntentFilter filter = new IntentFilter(
258                 mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX);
259
260         for(Integer id : mExistNotificationIds.keySet()) {
261             filter.addCategory(getCategoryFromNotificationId(id));
262         }
263         
264         try {
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());
270         }
271     }
272
273     private void unregisterReceiver() {
274         mView.getActivity().unregisterReceiver(mNotificationCloseReceiver);
275     }
276 }