com.github.kaiwinter.androidremotenotifications.RemoteNotifications Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of android-remote-notifications Show documentation
Show all versions of android-remote-notifications Show documentation
Pulls notifications from a remote JSON file and shows them in your app.
package com.github.kaiwinter.androidremotenotifications;
import android.app.AlertDialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;
import com.github.kaiwinter.androidremotenotifications.model.UpdatePolicy;
import com.github.kaiwinter.androidremotenotifications.model.UserNotification;
import com.github.kaiwinter.androidremotenotifications.model.impl.PersistentNotification;
import com.github.kaiwinter.androidremotenotifications.network.NotificationLoaderFinishListener;
import com.github.kaiwinter.androidremotenotifications.network.NotificationLoaderTask;
import com.github.kaiwinter.androidremotenotifications.persistence.NotificationStore;
import com.github.kaiwinter.androidremotenotifications.persistence.impl.SharedPreferencesStore;
import java.net.URL;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* The starting class of this library. Construct an instance with the URL to your JSON file on a server and call
* {@link #updateNotificationsFromServer(UpdatePolicy)}. This will load the configured notifications and store it in the
* libraries store. Call {@link #showPendingNotificationsToUser(boolean)} to automatically show notifications to the
* user by an {@link AlertDialog}.
*/
public final class RemoteNotifications {
public static final String TAG = "RemoteNotifications";
private static final String ARN_PREFERENCES_NAME = "arn";
private final URL serverUrl;
private final Context context;
private final NotificationStore preferenceStore;
private final Integer appVersionCode;
/**
* This guard is used for two things:
*
* - doing the notification update only once at a time
* - don't try to show notifications when update is running. In this case the show event is scheduled and {@link #scheduledShowEvent} is set to true.
*
* Guard to run the notification only one at a time.
*/
private AtomicBoolean notificationUpdateRunning = new AtomicBoolean(false);
/**
* This is set to true if {@link #showPendingNotificationsToUser} was called then an update of the notification was running.
* It is used to show the notifications after the update has finished.
*/
private AtomicBoolean scheduledShowEvent = new AtomicBoolean(false);
private boolean scheduledShowEventParameter;
/**
* Constructs a new {@link RemoteNotifications}. The Notifications from the server and the last server update timestamp will be
* stored in a Shared Preferences file with the name {@value #ARN_PREFERENCES_NAME}. To change the name use
* {@link #RemoteNotifications(Context, URL, String)}.
*
* @param context The application context
* @param serverUrl The absolute URL to the notification JSON on a server (or file system), not null
.
*/
public RemoteNotifications(Context context, URL serverUrl) {
this(context, serverUrl, ARN_PREFERENCES_NAME);
}
/**
* Constructs a new {@link RemoteNotifications}.
*
* @param context The application context
* @param serverUrl The absolute URL to the notification JSON on a server (or file system), not null
.
* @param sharedPreferenceName The file name to use for the Shared Preferences file.
*/
public RemoteNotifications(Context context, URL serverUrl, String sharedPreferenceName) {
if (serverUrl == null) {
throw new IllegalArgumentException("serverUrl must not be null");
}
if (context == null) {
throw new IllegalArgumentException("context must not be null");
}
if (sharedPreferenceName == null) {
throw new IllegalArgumentException("sharedPreferenceName must not be null");
}
this.serverUrl = serverUrl;
this.context = context;
SharedPreferences sharedPreferences = context.getSharedPreferences(sharedPreferenceName, Context.MODE_PRIVATE);
this.preferenceStore = new SharedPreferencesStore(sharedPreferences);
this.appVersionCode = getAppVersionCode(context);
}
private Integer getAppVersionCode(Context context) {
int versionCode;
try {
versionCode = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode;
} catch (NameNotFoundException e1) {
versionCode = -1;
}
return versionCode;
}
/**
* Shows all due notifications. If a server update of the notifications is running this call gets scheduled and is
* executed automatically after the update has finished.
*
* @see #showPendingNotificationsToUser(boolean)
*/
public void showPendingNotificationsToUser() {
showPendingNotificationsToUser(true);
}
/**
* Shows due notifications. If a server update of the notifications is running this call gets scheduled and is
* executed automatically after the update has finished.
*
* @param showAll if true
all pending notification dialogs will be shown (may result in bad user experience
* if several dialogs are popping up). If false
only one dialog will be shown on each call
* of this method.
*/
public void showPendingNotificationsToUser(boolean showAll) {
if (notificationUpdateRunning.get()) {
Log.w(TAG, "Notification update running, scheduling show event");
scheduledShowEvent.set(true);
// store passed parameter
scheduledShowEventParameter = showAll;
return;
}
Set persistentNotifications = preferenceStore.getPersistentNotifications();
Set updateNotifications = new HashSet<>();
for (PersistentNotification persistentNotification : persistentNotifications) {
if (persistentNotification.hasToBeShown(appVersionCode)) {
//notificationConsumer.show(context, persistentNotification.getNotification());
persistentNotification.getNotification().show(context);
persistentNotification.setLastShown(new Date());
persistentNotification.setShownCounter(persistentNotification.getShownCounter() + 1);
updateNotifications.add(persistentNotification);
if (!showAll) {
break;
}
}
}
if (!updateNotifications.isEmpty()) {
preferenceStore.updatePersistentNotification(updateNotifications);
}
}
/**
* Starts an AsyncTask which loads the notifications from the server, removes local notifications which doesn't exist anymore and adds new
* one.
*
* @param updatePolicy Use this parameter to reduce the number of server calls, not null
.
* @param listener a {@link NotificationLoaderFinishListener} which is called after the update of the notifications finished.
*/
public void updateNotificationsFromServer(UpdatePolicy updatePolicy, final NotificationLoaderFinishListener listener) {
if (updatePolicy == null) {
throw new IllegalArgumentException("updatePolicy must not be null");
}
Date date = preferenceStore.getLastServerUpdate();
boolean shouldUpdate = updatePolicy.shouldUpdate(date);
Log.v(TAG, "UpdatePolicy: " + updatePolicy.toString() + ", shouldUpdate: " + shouldUpdate);
if (!shouldUpdate) {
return;
}
boolean updateRunning = notificationUpdateRunning.getAndSet(true);
if (updateRunning) {
Log.w(TAG, "Notification update already running, skipping this update");
return;
}
NotificationLoaderFinishListener internalListener = new NotificationLoaderFinishListener() {
@Override
public void onDownloadFinished(Set notifications) {
Log.v(TAG, "Received " + notifications.size() + " notifications");
handleNotificationsFromServer(notifications);
notificationUpdateRunning.set(false);
preferenceStore.saveLastServerUpdate(new Date());
if (scheduledShowEvent.getAndSet(false)) {
Log.v(TAG, "executing scheduled show event");
showPendingNotificationsToUser(scheduledShowEventParameter);
}
// Call custom listener
if (listener != null) {
listener.onDownloadFinished(notifications);
}
}
};
NotificationLoaderTask notificationLoaderTask = new NotificationLoaderTask(serverUrl, internalListener);
notificationLoaderTask.execute();
}
/**
* Starts an AsyncTask which loads the notifications from the server, removes local notifications which doesn't exist anymore and adds new
* one.
*
* @param updatePolicy Use this parameter to reduce the number of server calls, not null
.
*/
public void updateNotificationsFromServer(UpdatePolicy updatePolicy) {
updateNotificationsFromServer(updatePolicy, null);
}
private void handleNotificationsFromServer(Set notifications) {
// Remove notifications from app which aren't anymore in the server notifications
Set persistentNotifications = preferenceStore.getPersistentNotifications();
boolean save = false;
for (Iterator iterator = persistentNotifications.iterator(); iterator.hasNext(); ) {
PersistentNotification persistentNotification = iterator.next();
if (!notifications.contains(persistentNotification.getNotification())) {
// Remove notification
iterator.remove();
save = true;
Log.v(TAG, "Removed Persistent Notification " + persistentNotification.toString());
}
}
// Add notifications from server which aren't in the app notification
for (UserNotification notification : notifications) {
PersistentNotification persistentNotification = new PersistentNotification(notification);
if (!persistentNotifications.contains(persistentNotification)) {
// Add new notification
persistentNotifications.add(persistentNotification);
save = true;
Log.v(TAG, "Added Persistent Notification " + persistentNotification.toString());
}
}
if (save) {
preferenceStore.replacePersistentNotifications(persistentNotifications);
}
}
/**
* @return the internally used store of the notifications
*/
public NotificationStore getPreferenceStore() {
return preferenceStore;
}
/**
* Convenience method to update and show notifications.
*
* @param context the application context
* @param url the URL of the JSON file
* @param updatePolicy the time between server updates
*/
public static void start(Context context, URL url, UpdatePolicy updatePolicy) {
RemoteNotifications remoteNotifications = new RemoteNotifications(context, url);
remoteNotifications.updateNotificationsFromServer(updatePolicy);
remoteNotifications.showPendingNotificationsToUser();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy