Upstream version 6.35.131.0
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / core / src / org / xwalk / core / 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;
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.XWalkContentsClientBridge;
23 import org.xwalk.core.XWalkNotificationService;
24 import org.xwalk.core.XWalkView;
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_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_";
38
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;
46
47     public XWalkNotificationServiceImpl(Context context, XWalkView view) {
48         mContext = context;
49         mView = 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>();
55     }
56
57     @Override
58     public void setBridge(XWalkContentsClientBridge bridge) {
59         mBridge = bridge;
60     }
61
62     private static String getCategoryFromNotificationId(int id) {
63         return XWALK_INTENT_CATEGORY_NOTIFICATION_PREFIX + id;
64     }
65
66     private void notificationChanged() {
67         unregisterReceiver();
68         if (mExistNotificationIds.isEmpty()) {
69             Log.i(TAG, "notifications are all cleared," +
70                     "unregister broadcast receiver for close pending intent");
71         } else {
72             registerReceiver();
73         }
74     }
75
76     private void registerReceiver() {
77         mNotificationCloseReceiver = new BroadcastReceiver() {
78             @Override
79             public void onReceive(Context context, Intent intent) {
80                 mView.onNewIntent(intent);
81             }
82         };
83         mNotificationCloseIntentFilter = new IntentFilter(
84                 mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX);
85         for(Integer id : mExistNotificationIds.keySet()) {
86             mNotificationCloseIntentFilter.addCategory(getCategoryFromNotificationId(id));
87         }
88         try {
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());
95         }
96     }
97
98     private void unregisterReceiver() {
99         if (mNotificationCloseReceiver != null) {
100             mView.getActivity().unregisterReceiver(mNotificationCloseReceiver);
101             mNotificationCloseReceiver = null;
102         }
103     }
104
105     @Override
106     public void shutdown() {
107         unregisterReceiver();
108         mBridge = null;
109     }
110
111     @Override
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);
121             return true;
122         } else if (intent.getAction().equals(
123                 mView.getActivity().getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX)) {
124             onNotificationClick(notificationId, processId, routeId);
125             return true;
126         }
127         return false;
128     }
129
130     @Override
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)
138                 return;
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;
146                 } else {
147                     targetWidth = originalWidth * targetHeight / originalHeight;
148                 }
149             }
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();
155             } else {
156                 notification = builder.getNotification();
157             }
158             doShowNotification(notificationId, notification);
159             mExistNotificationIds.put(notificationId, builder);
160         }
161     }
162
163     @Override
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);
179         Intent closeIntent =
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();
199         } else {
200             notification = builder.getNotification();
201         }
202         doShowNotification(notificationId, notification);
203         mExistNotificationIds.put(notificationId, builder);
204         notificationChanged();
205         onNotificationShown(notificationId, processId, routeId);
206     }
207
208     @Override
209     public void cancelNotification(int notificationId, int processId, int routeId) {
210         mNotificationManager.cancel(notificationId);
211         onNotificationClose(notificationId, false, processId, routeId);
212     }
213
214     public void doShowNotification(int id, Notification notification) {
215         mNotificationManager.notify(id, notification);
216     }
217
218     public void onNotificationShown(int notificationId, int processId, int routeId) {
219         if (mExistNotificationIds.containsKey(notificationId) && mBridge != null) {
220             mBridge.notificationDisplayed(notificationId, processId, routeId);
221         }
222     }
223
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);
230             }
231         }
232     }
233
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);
241             }
242         }
243     }
244 }