diff --git a/config.xml b/config.xml index d88e6ba..4d89f16 100644 --- a/config.xml +++ b/config.xml @@ -51,6 +51,7 @@ + diff --git a/src/android/BackgroundFetchHeadlessTask.java b/src/android/BackgroundFetchHeadlessTask.java new file mode 100644 index 0000000..12e2893 --- /dev/null +++ b/src/android/BackgroundFetchHeadlessTask.java @@ -0,0 +1,175 @@ +package com.transistorsoft.cordova.backgroundfetch; + +import android.app.Activity; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; + +import com.netsyms.BusinessMobile.R; +import com.transistorsoft.tsbackgroundfetch.BackgroundFetch; + +import android.graphics.Color; +import android.os.Build; +import android.provider.Settings; +import android.support.v4.app.NotificationCompat; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; + +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONArray; + +public class BackgroundFetchHeadlessTask implements HeadlessTask { + + private static final String PREFS_NAME = "NativeStorage"; + private static final String KEY = "accounts"; + private static String shownNotifications = ""; + public final String NOTIFICATION_CHANNEL_ID = "background-channel-id"; + + public static String getValue(Context context, String key, String defaultValue) { + SharedPreferences settings = context.getSharedPreferences(PREFS_NAME, Activity.MODE_PRIVATE); + return settings.getString(key, defaultValue); + } + + /** + * https://stackoverflow.com/a/34691486 + * + * @param urlString + * @return + * @throws IOException + */ + public static String getStringFromURL(String urlString) throws IOException { + HttpURLConnection urlConnection = null; + URL url = new URL(urlString); + urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setRequestMethod("GET"); + urlConnection.setReadTimeout(10000 /* milliseconds */); + urlConnection.setConnectTimeout(15000 /* milliseconds */); + urlConnection.setDoOutput(true); + urlConnection.connect(); + + BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream())); + StringBuilder sb = new StringBuilder(); + + String line; + while ((line = br.readLine()) != null) { + sb.append(line + "\n"); + } + br.close(); + + String jsonString = sb.toString(); + return jsonString; + } + + @Override + public void onFetch(Context context) { + Log.d(BackgroundFetch.TAG, "Notification Poller: onFetch"); + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + String accountjson = getValue(context, KEY, null).replaceAll("\\\\", ""); + accountjson = accountjson.substring(1, accountjson.length() - 1); + //Log.d(BackgroundFetch.TAG, "Notification Poller: Account JSON: " + accountjson); + try { + JSONArray accounts = new JSONArray(accountjson); + + for (int i = 0; i < accounts.length(); i++) { + try { + JSONObject acct = accounts.getJSONObject(i); + Log.d(BackgroundFetch.TAG, "Notification Poller: Account " + i + " URL: " + acct.getString("syncurl")); + String notificationString = getStringFromURL(acct.getString("syncurl") + "?key=" + acct.getString("key") + "&username=" + acct.getString("username") + "&action=checknotifications"); + //Log.d(BackgroundFetch.TAG, "Notification Poller: Account " + i + " JSON: " + notificationString); + JSONArray notifications = new JSONObject(notificationString).getJSONArray("notifications"); + Log.d(BackgroundFetch.TAG, "Notification Poller: Account " + i + " JSON parsed: " + notifications.length() + " notifications"); + for (int j = 0; j < notifications.length(); j++) { + JSONObject notif = notifications.getJSONObject(j); + + Log.d(BackgroundFetch.TAG, "Notification Poller: Procesing notification ID " + notif.getString("id") + " with title " + notif.getString("title")); + + if (notif.getBoolean("seen") || shownNotifications.contains("|" + i + ":" + notif.getString("id") + "|")) { + continue; + } + + Intent intent = new Intent(context, com.netsyms.BusinessMobile.MainActivity.class); + PendingIntent contentIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + + NotificationCompat.Builder b = new NotificationCompat.Builder(context); + + b.setAutoCancel(true) + .setDefaults(Notification.DEFAULT_ALL) + .setSmallIcon(R.drawable.ic_notification) + .setContentTitle(notif.getString("title")) + .setContentText(notif.getString("content")) + .setSound(Settings.System.DEFAULT_NOTIFICATION_URI) + .setContentIntent(contentIntent) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setVisibility(Notification.VISIBILITY_PRIVATE); + + // Create alternate notification for lockscreen + NotificationCompat.Builder lockb = new NotificationCompat.Builder(context); + if (notif.getBoolean("sensitive") == true) { + lockb + .setSmallIcon(R.drawable.ic_notification) + .setContentTitle("Contents hidden") + .setPriority(NotificationCompat.PRIORITY_DEFAULT); + } else { + lockb + .setSmallIcon(R.drawable.ic_notification) + .setContentTitle(notif.getString("title")) + .setContentText(notif.getString("content")) + .setPriority(NotificationCompat.PRIORITY_DEFAULT); + } + + b.setPublicVersion(lockb.build()); + + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + CharSequence name = "Background Notifications"; + int importance = NotificationManager.IMPORTANCE_DEFAULT; + NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, name, importance); + channel.enableLights(true); + channel.enableVibration(true); + channel.setLightColor(Color.rgb(33, 150, 243)); + // Register the channel with the system; you can't change the importance + // or other notification behaviors after this + b.setChannelId(NOTIFICATION_CHANNEL_ID); + notificationManager.createNotificationChannel(channel); + } + + + notificationManager.notify(Integer.parseInt(notif.getString("id")), b.build()); + + Log.d(BackgroundFetch.TAG, "Notification Poller: Shown notification " + notif.getString("id")); + + shownNotifications += "|" + i + ":" + notif.getString("id") + "|"; + } + } catch (Exception e) { + Log.d(BackgroundFetch.TAG, "Notification Poller: Exception: " + e.getMessage()); + Log.d(BackgroundFetch.TAG, "Notification Poller: Stack trace: " + Log.getStackTraceString(e)); + } + } + } catch (Exception e) { + Log.d(BackgroundFetch.TAG, "Notification Poller: Exception: " + e.getMessage()); + Log.d(BackgroundFetch.TAG, "Notification Poller: Stack trace: " + Log.getStackTraceString(e)); + } finally { + BackgroundFetch.getInstance(context).finish(); + } + } + } + ); + thread.start(); + } +} diff --git a/www/js/app.js b/www/js/app.js index fabb41b..d870b54 100644 --- a/www/js/app.js +++ b/www/js/app.js @@ -219,7 +219,8 @@ function displayNotifications(callback) { actions: [ {id: 'mark_read', title: "Mark Read"} ], - id: n.id + id: n.id, + lockscreen: !n.sensitive }]); shown_notifications.push(n.id); @@ -263,7 +264,9 @@ document.addEventListener("deviceready", function () { } cordova.plugins.notification.local.setDefaults({ - led: {color: '#2196F3'}, + led: true, + color: '#2196F3', + vibrate: true, smallIcon: "res://ic_notification" }); cordova.plugins.notification.local.on("mark_read", function (notification) {