Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / core / src / org / xwalk / core / client / XWalkDefaultNotificationService.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.client;
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 public class XWalkDefaultNotificationService implements XWalkNotificationService {
27     private static final String TAG = "XWalkDefaultNotificationService";
28
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_";
35
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;
43
44     public XWalkDefaultNotificationService(Context context, XWalkView view) {
45         mContext = context;
46         mView = 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>();
52     }
53
54     @Override
55     public void setBridge(XWalkContentsClientBridge bridge) {
56         mBridge = bridge;
57     }
58
59     private static String getCategoryFromNotificationId(int id) {
60         return XWALK_INTENT_CATEGORY_NOTIFICATION_PREFIX + id;
61     }
62
63     private void notificationChanged() {
64         unregisterReceiver();
65         if (mExistNotificationIds.isEmpty()) {
66             Log.i(TAG, "notifications are all cleared," +
67                     "unregister broadcast receiver for close pending intent");
68         } else {
69             registerReceiver();
70         }
71     }
72
73     private void registerReceiver() {
74         mNotificationCloseReceiver = new BroadcastReceiver() {
75             @Override
76             public void onReceive(Context context, Intent intent) {
77                 mView.onNewIntent(intent);
78             }
79         };
80         mNotificationCloseIntentFilter = new IntentFilter(
81                 mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX);
82         for(Integer id : mExistNotificationIds.keySet()) {
83             mNotificationCloseIntentFilter.addCategory(getCategoryFromNotificationId(id));
84         }
85         try {
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());
92         }
93     }
94
95     private void unregisterReceiver() {
96         if (mNotificationCloseReceiver != null) {
97             mView.getActivity().unregisterReceiver(mNotificationCloseReceiver);
98             mNotificationCloseReceiver = null;
99         }
100     }
101
102     @Override
103     public void shutdown() {
104         unregisterReceiver();
105         mBridge = null;
106     }
107
108     @Override
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);
118             return true;
119         } else if (intent.getAction().equals(
120                 mView.getActivity().getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX)) {
121             onNotificationClick(notificationId, processId, routeId);
122             return true;
123         }
124         return false;
125     }
126
127     @Override
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)
135                 return;
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;
143                 } else {
144                     targetWidth = originalWidth * targetHeight / originalHeight;
145                 }
146             }
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();
152             } else {
153                 notification = builder.getNotification();
154             }
155             doShowNotification(notificationId, notification);
156             mExistNotificationIds.put(notificationId, builder);
157         }
158     }
159
160     @Override
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);
176         Intent closeIntent =
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();
196         } else {
197             notification = builder.getNotification();
198         }
199         doShowNotification(notificationId, notification);
200         mExistNotificationIds.put(notificationId, builder);
201         notificationChanged();
202         onNotificationShown(notificationId, processId, routeId);
203     }
204
205     @Override
206     public void cancelNotification(int notificationId, int processId, int routeId) {
207         mNotificationManager.cancel(notificationId);
208         onNotificationClose(notificationId, false, processId, routeId);
209     }
210
211     public void doShowNotification(int id, Notification notification) {
212         mNotificationManager.notify(id, notification);
213     }
214
215     public void onNotificationShown(int notificationId, int processId, int routeId) {
216         if (mExistNotificationIds.containsKey(notificationId) && mBridge != null) {
217             mBridge.notificationDisplayed(notificationId, processId, routeId);
218         }
219     }
220
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);
227             }
228         }
229     }
230
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);
238             }
239         }
240     }
241 }