diff --git a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/logic/ResourcesUpdateTool.java b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/logic/ResourcesUpdateTool.java index 8ac3d66b7a..5de4b018e5 100644 --- a/android/KMEA/app/src/main/java/com/tavultesoft/kmea/logic/ResourcesUpdateTool.java +++ b/android/KMEA/app/src/main/java/com/tavultesoft/kmea/logic/ResourcesUpdateTool.java @@ -1,6 +1,5 @@ package com.tavultesoft.kmea.logic; - import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; @@ -13,6 +12,7 @@ import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.util.Log; import android.widget.Toast; import androidx.appcompat.app.AlertDialog; @@ -21,7 +21,6 @@ import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat.Builder; import androidx.core.app.NotificationManagerCompat; - import com.tavultesoft.kmea.KMKeyboardDownloaderActivity; import com.tavultesoft.kmea.KeyboardPickerActivity; import com.tavultesoft.kmea.KMManager; @@ -39,8 +38,12 @@ import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import org.json.JSONException; +import org.json.JSONObject; + public class ResourcesUpdateTool implements KeyboardEventHandler.OnKeyboardDownloadEventListener, CloudRepository.UpdateHandler{ + private static final String TAG = "ResourceUpdateTool"; /** * Force resource update. @@ -63,6 +66,16 @@ public class ResourcesUpdateTool implements KeyboardEventHandler.OnKeyboardDownl */ public static final String PREF_KEY_LAST_UPDATE_CHECK = "lastUpdateCheck"; + /** + * Preference key for ignored notifications + */ + public static final String PREF_KEY_IGNORE_NOTIFICATIONS = "ignoredNotifications"; + + /** + * Months to ignore update notification + */ + public static final int MONTHS_TO_IGNORE_NOTIFICATION = 3; + private static final class OngoingUpdate { Integer notificationid; @@ -269,6 +282,84 @@ public class ResourcesUpdateTool implements KeyboardEventHandler.OnKeyboardDownl addOpenUpdate(createKeyboardId(langid, kbid), null, theResourceBundle); } } + + /** + * Check shared preference to see if an update notification should be ignored. + * The window is MONTHS_TO_IGNORE_NOTIFICATION from the last time the notification was ignored. + * @param id keyboard or lexical model ID + * @return true if the notification should be ignored + */ + private boolean shouldIgnoreNotification(String id) { + SharedPreferences prefs = currentContext.getSharedPreferences(currentContext.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + String ignoredNotificationsStr = prefs.getString(PREF_KEY_IGNORE_NOTIFICATIONS, null); + + /* + * Preference is a JSON Object (as a string) + * { PREF_KEY_IGNORE_NOTIFICATIONS : + * { id1 : time1 ignored, + * id2 : time2 ignored + * } + * } + */ + JSONObject ignoredNotificationsObj; + if (ignoredNotificationsStr != null) { + try { + ignoredNotificationsObj = new JSONObject(ignoredNotificationsStr); + + Long lastIgnoredTime = ignoredNotificationsObj.optLong(id, 0); + if (lastIgnoredTime > 0) { + Calendar now = Calendar.getInstance(); + Calendar ignoreUntilTime = Calendar.getInstance(); + ignoreUntilTime.setTime(new Date(lastIgnoredTime)); + ignoreUntilTime.add(Calendar.MONTH, MONTHS_TO_IGNORE_NOTIFICATION); + if (now.compareTo(ignoreUntilTime) < 0) { + return true; + } + } + } catch (JSONException e) { + Log.e(TAG, "JSON Exception parsing ignoreNotifications preference"); + } + } + + return false; + } + + /** + * Update preference to ignore notifications for keyboard / lexical model ID + * @param id : keyboard or lexical model ID to ignore for MONTHS_TO_IGNORE_NOTIFICATION months + */ + private void setPrefKeyIgnoreNotifications(String id) { + SharedPreferences prefs = currentContext.getSharedPreferences( + currentContext.getString(R.string.kma_prefs_name), Context.MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + String ignoredNotificationsStr = prefs.getString(PREF_KEY_IGNORE_NOTIFICATIONS, null); + + /* + * Preference is a JSON Object (stored as a string) + * { PREF_KEY_IGNORE_NOTIFICATIONS : + * { id1 : time1 ignored, + * id2 : time2 ignored + * } + * } + */ + JSONObject ignoredNotificationsObj; + try { + if (ignoredNotificationsStr == null) { + ignoredNotificationsObj = new JSONObject(); + } else { + ignoredNotificationsObj = new JSONObject(ignoredNotificationsStr); + } + + Calendar now = Calendar.getInstance(); + ignoredNotificationsObj.put(id, now.getTime().getTime()); + editor.putString(PREF_KEY_IGNORE_NOTIFICATIONS, ignoredNotificationsObj.toString()); + editor.commit(); + } catch (JSONException e) { + Log.e(TAG, "JSON Exception updating ignoreNotifications preference"); + } + } + /** * send update notification. * @param theResourceBundle the bundle @@ -290,13 +381,25 @@ public class ResourcesUpdateTool implements KeyboardEventHandler.OnKeyboardDownl String modelid = theResourceBundle.getString(KMKeyboardDownloaderActivity.ARG_MODEL_ID); String modelName = theResourceBundle.getString(KMKeyboardDownloaderActivity.ARG_MODEL_NAME); message = currentContext.getString(R.string.dictionary_update_message, langName, modelName); - addOpenUpdate(createLexicalModelId(langid,modelid),notification_id, theResourceBundle); + if (!shouldIgnoreNotification(modelid)) { + addOpenUpdate(createLexicalModelId(langid, modelid), notification_id, theResourceBundle); + } else { + // Update notification should be ignored + notificationManager.cancel(notification_id); + return; + } } else { String kbid = theResourceBundle.getString(KMKeyboardDownloaderActivity.ARG_KB_ID); String kbName = theResourceBundle.getString(KMKeyboardDownloaderActivity.ARG_KB_NAME); message = currentContext.getString(R.string.keyboard_update_message, langName, kbName); - addOpenUpdate(createKeyboardId(langid,kbid),notification_id, theResourceBundle); + if (!shouldIgnoreNotification(kbid)) { + addOpenUpdate(createKeyboardId(langid, kbid), notification_id, theResourceBundle); + } else { + // Update notification should be ignored + notificationManager.cancel(notification_id); + return; + } } @@ -465,6 +568,7 @@ public class ResourcesUpdateTool implements KeyboardEventHandler.OnKeyboardDownl public void cancelKeyboardUpdate(String aLangId, String aKbId) { + setPrefKeyIgnoreNotifications(aKbId); removeOpenUpdate(createKeyboardId(aLangId,aKbId)); if(openUpdates.isEmpty()) checkingUpdates = false; @@ -472,6 +576,7 @@ public class ResourcesUpdateTool implements KeyboardEventHandler.OnKeyboardDownl public void cancelLexicalModelUpdate(String aLangId, String aModelId) { + setPrefKeyIgnoreNotifications(aModelId); removeOpenUpdate(createLexicalModelId(aLangId,aModelId)); if(openUpdates.isEmpty()) checkingUpdates = false; diff --git a/android/history.md b/android/history.md index 8df1a65d2f..b220a2e4c9 100644 --- a/android/history.md +++ b/android/history.md @@ -9,10 +9,12 @@ * Check for keyboard updates during keyman startup (#2335) * Show available keyboard updates as android system notifications (#2335) * Add update indicator icon to inform user about updates and install updates in keyman app (#2335) + * Add preference so update notifications can be ignored 3 months (#2412) * Changes: * Update target Android SDK version to 29 (#2279) * Add simple UI tests for keyboard picker and keyboard info screens (#2326) * Add example dictionary to KMSample1 project (#2369) + * Prevent lower-cased API returns from causing mismatches (#2404) * Bug fix: * Sanitize the app version to `#.#.#` for the API cloud query (#2319) * Add linting to Debug builds and resolve lint errors (#2305)