Skip to content

Commit 9d6de64

Browse files
authored
Merge pull request #112 from CatalystCode/thcao/android-unit-tests
[Android] Refactoring Notification Handler and adding unit tests
2 parents 103e68c + 1eb054f commit 9d6de64

File tree

7 files changed

+767
-212
lines changed

7 files changed

+767
-212
lines changed

android/src/main/java/com/azure/reactnative/notificationhub/NotificationHubUtil.java

Lines changed: 192 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
package com.azure.reactnative.notificationhub;
22

3+
import android.app.PendingIntent;
34
import android.content.Context;
45
import android.content.Intent;
56
import android.content.SharedPreferences;
7+
import android.content.res.Resources;
8+
import android.media.RingtoneManager;
9+
import android.net.Uri;
610
import android.os.Bundle;
11+
import android.util.Log;
12+
13+
import androidx.core.app.NotificationCompat;
714

815
import java.util.Arrays;
916
import java.util.HashSet;
@@ -14,10 +21,33 @@
1421
import com.facebook.react.bridge.ReactContext;
1522
import com.microsoft.windowsazure.messaging.NotificationHub;
1623

24+
import org.json.JSONArray;
1725
import org.json.JSONException;
1826
import org.json.JSONObject;
1927

28+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.ERROR_COVERT_ACTIONS;
29+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.ERROR_GET_ACTIONS_ARRAY;
30+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.KEY_INTENT_NOTIFICATION;
31+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.KEY_REMOTE_NOTIFICATION_ACTION;
32+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.KEY_REMOTE_NOTIFICATION_ACTIONS;
33+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.KEY_REMOTE_NOTIFICATION_COLDSTART;
34+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.KEY_REMOTE_NOTIFICATION_FOREGROUND;
35+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.KEY_REMOTE_NOTIFICATION_SMALL_ICON;
36+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.KEY_REMOTE_NOTIFICATION_SOUND_NAME;
37+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.KEY_REMOTE_NOTIFICATION_USER_INTERACTION;
38+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.REMOTE_NOTIFICATION_PRIORITY_HIGH;
39+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.REMOTE_NOTIFICATION_PRIORITY_LOW;
40+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.REMOTE_NOTIFICATION_PRIORITY_MAX;
41+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.REMOTE_NOTIFICATION_PRIORITY_MIN;
42+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.REMOTE_NOTIFICATION_PRIORITY_NORMAL;
43+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.RESOURCE_DEF_TYPE_MIPMAP;
44+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.RESOURCE_DEF_TYPE_RAW;
45+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.RESOURCE_NAME_LAUNCHER;
46+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.RESOURCE_NAME_NOTIFICATION;
47+
2048
public class NotificationHubUtil {
49+
public static final String TAG = "NotificationHubUtil";
50+
2151
private static NotificationHubUtil sharedNotificationHubUtilInstance = null;
2252

2353
private static final String SHARED_PREFS_NAME = "com.azure.reactnative.notificationhub.NotificationHubUtil";
@@ -149,6 +179,10 @@ public boolean getAppIsForeground() {
149179
return mIsForeground;
150180
}
151181

182+
public void runInWorkerThread(Runnable runnable) {
183+
mPool.execute(runnable);
184+
}
185+
152186
public NotificationHub createNotificationHub(String hubName, String connectionString, ReactContext reactContext) {
153187
NotificationHub hub = new NotificationHub(hubName, connectionString, reactContext);
154188
return hub;
@@ -175,8 +209,164 @@ public Intent createBroadcastIntent(String action, JSONObject json) {
175209
return intent;
176210
}
177211

178-
public void runInWorkerThread(Runnable runnable) {
179-
mPool.execute(runnable);
212+
public Class getMainActivityClass(Context context) {
213+
String packageName = context.getPackageName();
214+
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
215+
String className = launchIntent.getComponent().getClassName();
216+
try {
217+
return Class.forName(className);
218+
} catch (ClassNotFoundException e) {
219+
e.printStackTrace();
220+
return null;
221+
}
222+
}
223+
224+
public int getNotificationCompatPriority(String priorityString) {
225+
int priority = NotificationCompat.PRIORITY_DEFAULT;
226+
if (priorityString != null) {
227+
switch (priorityString.toLowerCase()) {
228+
case REMOTE_NOTIFICATION_PRIORITY_MAX:
229+
priority = NotificationCompat.PRIORITY_MAX;
230+
break;
231+
case REMOTE_NOTIFICATION_PRIORITY_HIGH:
232+
priority = NotificationCompat.PRIORITY_HIGH;
233+
break;
234+
case REMOTE_NOTIFICATION_PRIORITY_LOW:
235+
priority = NotificationCompat.PRIORITY_LOW;
236+
break;
237+
case REMOTE_NOTIFICATION_PRIORITY_MIN:
238+
priority = NotificationCompat.PRIORITY_MIN;
239+
break;
240+
case REMOTE_NOTIFICATION_PRIORITY_NORMAL:
241+
priority = NotificationCompat.PRIORITY_DEFAULT;
242+
break;
243+
}
244+
}
245+
246+
return priority;
247+
}
248+
249+
public int getSmallIcon(Bundle bundle, Resources res, String packageName) {
250+
int smallIconResId;
251+
String smallIcon = bundle.getString(KEY_REMOTE_NOTIFICATION_SMALL_ICON);
252+
253+
if (smallIcon != null) {
254+
smallIconResId = res.getIdentifier(smallIcon, RESOURCE_DEF_TYPE_MIPMAP, packageName);
255+
} else {
256+
smallIconResId = res.getIdentifier(RESOURCE_NAME_NOTIFICATION, RESOURCE_DEF_TYPE_MIPMAP, packageName);
257+
}
258+
259+
if (smallIconResId == 0) {
260+
smallIconResId = res.getIdentifier(RESOURCE_NAME_LAUNCHER, RESOURCE_DEF_TYPE_MIPMAP, packageName);
261+
262+
if (smallIconResId == 0) {
263+
smallIconResId = android.R.drawable.ic_dialog_info;
264+
}
265+
}
266+
267+
return smallIconResId;
268+
}
269+
270+
public int getLargeIcon(Bundle bundle, String largeIcon, Resources res, String packageName) {
271+
int largeIconResId;
272+
273+
if (largeIcon != null) {
274+
largeIconResId = res.getIdentifier(largeIcon, RESOURCE_DEF_TYPE_MIPMAP, packageName);
275+
} else {
276+
largeIconResId = res.getIdentifier(RESOURCE_NAME_LAUNCHER, RESOURCE_DEF_TYPE_MIPMAP, packageName);
277+
}
278+
279+
return largeIconResId;
280+
}
281+
282+
public Uri getSoundUri(Context context, Bundle bundle) {
283+
Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
284+
String soundName = bundle.getString(KEY_REMOTE_NOTIFICATION_SOUND_NAME);
285+
if (soundName != null) {
286+
if (!"default".equalsIgnoreCase(soundName)) {
287+
288+
// sound name can be full filename, or just the resource name.
289+
// So the strings 'my_sound.mp3' AND 'my_sound' are accepted
290+
// The reason is to make the iOS and android javascript interfaces compatible
291+
292+
int resId;
293+
if (context.getResources().getIdentifier(soundName, RESOURCE_DEF_TYPE_RAW, context.getPackageName()) != 0) {
294+
resId = context.getResources().getIdentifier(soundName, RESOURCE_DEF_TYPE_RAW, context.getPackageName());
295+
} else {
296+
soundName = soundName.substring(0, soundName.lastIndexOf('.'));
297+
resId = context.getResources().getIdentifier(soundName, RESOURCE_DEF_TYPE_RAW, context.getPackageName());
298+
}
299+
300+
soundUri = Uri.parse("android.resource://" + context.getPackageName() + "/" + resId);
301+
}
302+
}
303+
304+
return soundUri;
305+
}
306+
307+
public Intent createNotificationIntent(Context context, Bundle bundle, Class intentClass) {
308+
Intent intent = new Intent(context, intentClass);
309+
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
310+
bundle.putBoolean(KEY_REMOTE_NOTIFICATION_FOREGROUND, true);
311+
bundle.putBoolean(KEY_REMOTE_NOTIFICATION_USER_INTERACTION, false);
312+
bundle.putBoolean(KEY_REMOTE_NOTIFICATION_COLDSTART, false);
313+
intent.putExtra(KEY_INTENT_NOTIFICATION, bundle);
314+
315+
return intent;
316+
}
317+
318+
public void processNotificationActions(Context context, Bundle bundle,
319+
NotificationCompat.Builder notification,
320+
int notificationID) {
321+
JSONArray actionsArray = null;
322+
try {
323+
actionsArray = bundle.getString(KEY_REMOTE_NOTIFICATION_ACTIONS) != null ?
324+
new JSONArray(bundle.getString(KEY_REMOTE_NOTIFICATION_ACTIONS)) : null;
325+
} catch (JSONException e) {
326+
Log.e(TAG, ERROR_COVERT_ACTIONS, e);
327+
}
328+
329+
if (actionsArray != null) {
330+
// No icon for now. The icon value of 0 shows no icon.
331+
int icon = 0;
332+
333+
// Add button for each actions.
334+
for (int i = 0; i < actionsArray.length(); i++) {
335+
String action;
336+
try {
337+
action = actionsArray.getString(i);
338+
} catch (JSONException e) {
339+
Log.e(TAG, ERROR_GET_ACTIONS_ARRAY, e);
340+
continue;
341+
}
342+
343+
Intent actionIntent = new Intent();
344+
actionIntent.setAction(context.getPackageName() + "." + action);
345+
// Add "action" for later identifying which button gets pressed.
346+
bundle.putString(KEY_REMOTE_NOTIFICATION_ACTION, action);
347+
actionIntent.putExtra(KEY_INTENT_NOTIFICATION, bundle);
348+
PendingIntent pendingActionIntent = PendingIntent.getBroadcast(context, notificationID, actionIntent,
349+
PendingIntent.FLAG_UPDATE_CURRENT);
350+
notification.addAction(icon, action, pendingActionIntent);
351+
}
352+
}
353+
}
354+
355+
public NotificationCompat.Builder initNotificationCompatBuilder(Context context,
356+
String notificationChannelID,
357+
String title,
358+
CharSequence ticker,
359+
int visibility,
360+
int priority,
361+
boolean autoCancel) {
362+
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, notificationChannelID)
363+
.setContentTitle(title)
364+
.setTicker(ticker)
365+
.setVisibility(visibility)
366+
.setPriority(priority)
367+
.setAutoCancel(autoCancel);
368+
369+
return notificationBuilder;
180370
}
181371

182372
private String getPref(Context context, String key) {

android/src/main/java/com/azure/reactnative/notificationhub/ReactNativeFirebaseMessagingService.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
import com.google.firebase.messaging.FirebaseMessagingService;
1212
import com.google.firebase.messaging.RemoteMessage;
1313

14+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.KEY_REMOTE_NOTIFICATION_COLDSTART;
15+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.KEY_REMOTE_NOTIFICATION_FOREGROUND;
16+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.KEY_REMOTE_NOTIFICATION_USER_INTERACTION;
17+
1418
public class ReactNativeFirebaseMessagingService extends FirebaseMessagingService {
1519

1620
private static final String TAG = "ReactNativeFMS";
@@ -67,9 +71,9 @@ public void onMessageReceived(RemoteMessage remoteMessage) {
6771

6872
Bundle bundle = remoteMessage.toIntent().getExtras();
6973
if (notificationHubUtil.getAppIsForeground()) {
70-
bundle.putBoolean("foreground", true);
71-
bundle.putBoolean("userInteraction", false);
72-
bundle.putBoolean("coldstart", false);
74+
bundle.putBoolean(KEY_REMOTE_NOTIFICATION_FOREGROUND, true);
75+
bundle.putBoolean(KEY_REMOTE_NOTIFICATION_USER_INTERACTION, false);
76+
bundle.putBoolean(KEY_REMOTE_NOTIFICATION_COLDSTART, false);
7377
} else {
7478
ReactNativeNotificationsHandler.sendNotification(this, bundle, notificationChannelID);
7579
}

android/src/main/java/com/azure/reactnative/notificationhub/ReactNativeNotificationHubModule.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
import com.facebook.react.bridge.ReadableMap;
2727
import com.facebook.react.bridge.UiThreadUtil;
2828

29+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.KEY_INTENT_NOTIFICATION;
30+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.KEY_REMOTE_NOTIFICATION_COLDSTART;
31+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.KEY_REMOTE_NOTIFICATION_FOREGROUND;
32+
import static com.azure.reactnative.notificationhub.ReactNativeNotificationsHandler.KEY_REMOTE_NOTIFICATION_USER_INTERACTION;
33+
2934
public class ReactNativeNotificationHubModule extends ReactContextBaseJavaModule implements
3035
ActivityEventListener, LifecycleEventListener {
3136
public static final String AZURE_NOTIFICATION_HUB_NAME = "AzureNotificationHub";
@@ -183,12 +188,12 @@ public void onHostResume() {
183188
if (activity != null) {
184189
Intent intent = activity.getIntent();
185190
if (intent != null) {
186-
Bundle bundle = intent.getBundleExtra("notification");
191+
Bundle bundle = intent.getBundleExtra(KEY_INTENT_NOTIFICATION);
187192
if (bundle != null) {
188-
intent.removeExtra("notification");
189-
bundle.putBoolean("foreground", false);
190-
bundle.putBoolean("userInteraction", true);
191-
bundle.putBoolean("coldstart", true);
193+
intent.removeExtra(KEY_INTENT_NOTIFICATION);
194+
bundle.putBoolean(KEY_REMOTE_NOTIFICATION_FOREGROUND, false);
195+
bundle.putBoolean(KEY_REMOTE_NOTIFICATION_USER_INTERACTION, true);
196+
bundle.putBoolean(KEY_REMOTE_NOTIFICATION_COLDSTART, true);
192197
ReactNativeNotificationsHandler.sendBroadcast(
193198
mReactContext, bundle, NOTIFICATION_DELAY_ON_START);
194199
}
@@ -207,10 +212,10 @@ public void onHostDestroy() {
207212

208213
@Override
209214
public void onNewIntent(Intent intent) {
210-
Bundle bundle = intent.getBundleExtra("notification");
215+
Bundle bundle = intent.getBundleExtra(KEY_INTENT_NOTIFICATION);
211216
if (bundle != null) {
212-
bundle.putBoolean("foreground", false);
213-
bundle.putBoolean("userInteraction", true);
217+
bundle.putBoolean(KEY_REMOTE_NOTIFICATION_FOREGROUND, false);
218+
bundle.putBoolean(KEY_REMOTE_NOTIFICATION_USER_INTERACTION, true);
214219
ReactNativeNotificationsHandler.sendBroadcast(
215220
mReactContext, bundle, NOTIFICATION_DELAY_ON_START);
216221
}

0 commit comments

Comments
 (0)