Upstream version 8.36.161.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 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;
45
46     public XWalkNotificationServiceImpl(Context context, XWalkViewInternal view) {
47         mContext = context;
48         mView = 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>();
54     }
55
56     @Override
57     public void setBridge(XWalkContentsClientBridge bridge) {
58         mBridge = bridge;
59     }
60
61     private static String getCategoryFromNotificationId(int id) {
62         return XWALK_INTENT_CATEGORY_NOTIFICATION_PREFIX + id;
63     }
64
65     private void notificationChanged() {
66         unregisterReceiver();
67         if (mExistNotificationIds.isEmpty()) {
68             Log.i(TAG, "notifications are all cleared," +
69                     "unregister broadcast receiver for close pending intent");
70         } else {
71             registerReceiver();
72         }
73     }
74
75     private void registerReceiver() {
76         mNotificationCloseReceiver = new BroadcastReceiver() {
77             @Override
78             public void onReceive(Context context, Intent intent) {
79                 mView.onNewIntent(intent);
80             }
81         };
82         mNotificationCloseIntentFilter = new IntentFilter(
83                 mView.getActivity().getPackageName() + XWALK_ACTION_CLOSE_NOTIFICATION_SUFFIX);
84         for(Integer id : mExistNotificationIds.keySet()) {
85             mNotificationCloseIntentFilter.addCategory(getCategoryFromNotificationId(id));
86         }
87         try {
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());
94         }
95     }
96
97     private void unregisterReceiver() {
98         if (mNotificationCloseReceiver != null) {
99             mView.getActivity().unregisterReceiver(mNotificationCloseReceiver);
100             mNotificationCloseReceiver = null;
101         }
102     }
103
104     @Override
105     public void shutdown() {
106         unregisterReceiver();
107         mBridge = null;
108     }
109
110     @Override
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);
119             return true;
120         } else if (intent.getAction().equals(
121                 mView.getActivity().getPackageName() + XWALK_ACTION_CLICK_NOTIFICATION_SUFFIX)) {
122             onNotificationClick(notificationId, delegate);
123             return true;
124         }
125         return false;
126     }
127
128     @Override
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)
136                 return;
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;
144                 } else {
145                     targetWidth = originalWidth * targetHeight / originalHeight;
146                 }
147             }
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();
153             } else {
154                 notification = builder.getNotification();
155             }
156             doShowNotification(notificationId, notification);
157             mExistNotificationIds.put(notificationId, builder);
158         }
159     }
160
161     @Override
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);
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_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();
195         } else {
196             notification = builder.getNotification();
197         }
198         doShowNotification(notificationId, notification);
199         mExistNotificationIds.put(notificationId, builder);
200         notificationChanged();
201         onNotificationShown(notificationId, delegate);
202     }
203
204     @Override
205     public void cancelNotification(int notificationId, long delegate) {
206         mNotificationManager.cancel(notificationId);
207         onNotificationClose(notificationId, false, delegate);
208     }
209
210     public void doShowNotification(int id, Notification notification) {
211         mNotificationManager.notify(id, notification);
212     }
213
214     public void onNotificationShown(int notificationId, long delegate) {
215         if (mExistNotificationIds.containsKey(notificationId) && mBridge != null) {
216             mBridge.notificationDisplayed(delegate);
217         }
218     }
219
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);
226             }
227         }
228     }
229
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);
237             }
238         }
239     }
240 }