From eab497ff7df1876629bf830364de6ba4211fc34b Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Thu, 13 Feb 2025 09:34:10 +0700 Subject: [PATCH 01/15] feat(android): add DownloadManager enablement check, related custom exception --- .../keyman/engine/cloud/CloudDownloadMgr.java | 19 +++++++++--- .../DownloadManagerDisabledException.java | 7 +++++ .../keyman/engine/data/CloudRepository.java | 14 +++++++-- .../engine/logic/ResourcesUpdateTool.java | 2 ++ .../keyman/engine/util/DownloadFileUtils.java | 31 +++++++++++++++++++ 5 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 android/KMEA/app/src/main/java/com/keyman/engine/cloud/DownloadManagerDisabledException.java diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java index 8a25ab09ba..edf067221e 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java @@ -5,11 +5,13 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.util.Log; import com.keyman.engine.util.KMLog; +import com.keyman.engine.util.DownloadFileUtils; import java.io.File; import java.util.HashMap; @@ -210,7 +212,7 @@ public class CloudDownloadMgr{ public void executeAsDownload(Context aContext, String aDownloadIdentifier, ModelType aTargetModel, ICloudDownloadCallback aCallback, - CloudApiTypes.CloudApiParam... params) + CloudApiTypes.CloudApiParam... params) throws DownloadManagerDisabledException { if(!isInitialized) { Log.w(TAG, "DownloadManager not initialized. Initializing CloudDownloadMgr."); @@ -219,16 +221,23 @@ public class CloudDownloadMgr{ KMLog.LogBreadcrumb("CloudDownloadMgr", "CloudDownloadMgr.executeAsDownload() called; already initialized", true); } + DownloadManager downloadManager = DownloadFileUtils.getDownloadManager(aContext); + if(downloadManager == null) { + aCallback.initializeContext(aContext); + + // Signal the failure state; we literally can't do this right now! + // Fortunately, that does mean there's no need to signal via callback. + // + // Unique custom error so it's easy to explicitly filter. + throw new DownloadManagerDisabledException(); + } + synchronized (downloadSetByDownloadIdentifier) { if (alreadyDownloadingData(aDownloadIdentifier) || params == null) { return; } - DownloadManager downloadManager = (DownloadManager) aContext.getSystemService(Context.DOWNLOAD_SERVICE); - if(downloadManager==null) - throw new IllegalStateException("DownloadManager is not available"); - aCallback.initializeContext(aContext); CloudApiTypes.CloudDownloadSet _downloadSet = diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/DownloadManagerDisabledException.java b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/DownloadManagerDisabledException.java new file mode 100644 index 0000000000..2011ad0eb0 --- /dev/null +++ b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/DownloadManagerDisabledException.java @@ -0,0 +1,7 @@ +package com.keyman.engine.cloud; + +public class DownloadManagerDisabledException extends RuntimeException { + DownloadManagerDisabledException() { + super("System service DownloadManager is not available and cannot facilitate downloads or queries."); + } +} \ No newline at end of file diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/data/CloudRepository.java b/android/KMEA/app/src/main/java/com/keyman/engine/data/CloudRepository.java index 4aad0a4e9f..9846dca149 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/data/CloudRepository.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/data/CloudRepository.java @@ -13,12 +13,14 @@ import com.keyman.engine.KMManager; import com.keyman.engine.KeyboardPickerActivity; import com.keyman.engine.R; import com.keyman.engine.cloud.CloudApiTypes; +import com.keyman.engine.cloud.DownloadManagerDisabledException; import com.keyman.engine.cloud.impl.CloudCatalogDownloadCallback; import com.keyman.engine.cloud.impl.CloudCatalogDownloadReturns; import com.keyman.engine.cloud.CloudDataJsonUtil; import com.keyman.engine.cloud.CloudDownloadMgr; import com.keyman.engine.packages.JSONUtils; import com.keyman.engine.util.BCP47; +import com.keyman.engine.util.DownloadFileUtils; import com.keyman.engine.util.KMLog; import com.keyman.engine.util.VersionUtils; @@ -366,6 +368,10 @@ public class CloudRepository { * @param onFailure A callback to be triggered upon failure of a query. */ private void downloadMetaDataFromServer(@NonNull Context context, UpdateHandler updateHandler, Runnable onSuccess, Runnable onFailure) { + if(DownloadFileUtils.getDownloadManager(context) == null) { + onFailure.run(); + return; + } boolean cacheValid = getCacheValidity(context); // For local and PR test builds, force download of metadata @@ -405,8 +411,12 @@ public class CloudRepository { BaseActivity.makeToast(context, R.string.catalog_download_is_running_in_background, Toast.LENGTH_SHORT); } else { updateIsRunning = true; - CloudDownloadMgr.getInstance().executeAsDownload( - context, DOWNLOAD_IDENTIFIER_CATALOGUE, memCachedDataset, _download_callback, params); + try { + CloudDownloadMgr.getInstance().executeAsDownload( + context, DOWNLOAD_IDENTIFIER_CATALOGUE, memCachedDataset, _download_callback, params); + } catch (DownloadManagerDisabledException e) { + onFailure.run(); + } } } } diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/logic/ResourcesUpdateTool.java b/android/KMEA/app/src/main/java/com/keyman/engine/logic/ResourcesUpdateTool.java index 9f02480bf2..8ff46bb0b7 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/logic/ResourcesUpdateTool.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/logic/ResourcesUpdateTool.java @@ -122,6 +122,8 @@ public class ResourcesUpdateTool implements KeyboardEventHandler.OnKeyboardDownl return; } + // Warning: can be run by our system keyboard, which will attempt to + // display the toast if it or the app is visible! BaseActivity.makeToast(currentContext, R.string.update_check_unavailable, Toast.LENGTH_SHORT); lastUpdateCheck = Calendar.getInstance(); updateCheckFailed = true; diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/util/DownloadFileUtils.java b/android/KMEA/app/src/main/java/com/keyman/engine/util/DownloadFileUtils.java index c16d3d381d..96ee696074 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/util/DownloadFileUtils.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/util/DownloadFileUtils.java @@ -3,7 +3,9 @@ */ package com.keyman.engine.util; +import android.app.DownloadManager; import android.content.Context; +import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.provider.OpenableColumns; @@ -21,6 +23,35 @@ import java.io.InputStream; public final class DownloadFileUtils { private static final String TAG = "DownloadFileUtils"; + private static final String DOWNLOAD_MANAGER_PACKAGE_NAME = "com.android.providers.downloads"; + + /** + * Determines whether or not Android's `DownloadManager` service is active; + * downloads and cloud queries are impossible when it's disabled. + * + * This solution is based heavily on + * https://gist.github.com/Folyd/b9412bb6e2b06eb511f7. + * @return + */ + public static DownloadManager getDownloadManager(Context context) { + DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); + if(downloadManager==null) { + return null; + } + + int state = context.getPackageManager().getApplicationEnabledSetting(DOWNLOAD_MANAGER_PACKAGE_NAME); + + if( + state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED || + state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER || + state == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED + ) { + return null; + }; + + return downloadManager; + } + /** * Small class for returning information about a file downloaded via DownloadManager. */ From c3caf7ffffcda7aed026e1b85de4def0b8c2a371 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Thu, 13 Feb 2025 10:14:42 +0700 Subject: [PATCH 02/15] fix(android): prevent update-checks from instantly crashing app + system keyboard Fixes: #11568 --- .../main/java/com/keyman/android/SystemKeyboard.java | 10 +++++++++- .../main/java/com/tavultesoft/kmapro/MainActivity.java | 2 ++ android/KMAPro/kMAPro/src/main/res/values/strings.xml | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java index 5ca3de5807..690f36d9ed 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java +++ b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java @@ -4,6 +4,9 @@ package com.keyman.android; +import com.keyman.engine.cloud.DownloadManagerDisabledException; +import com.keyman.engine.util.DownloadFileUtils; +import com.keyman.engine.util.KMLog; import com.tavultesoft.kmapro.AdjustLongpressDelayActivity; import com.tavultesoft.kmapro.BuildConfig; import com.tavultesoft.kmapro.DefaultLanguageResource; @@ -84,7 +87,12 @@ public class SystemKeyboard extends InputMethodService implements OnKeyboardEven boolean mayHaveHapticFeedback = prefs.getBoolean(KeymanSettingsActivity.hapticFeedbackKey, false); KMManager.setHapticFeedback(mayHaveHapticFeedback); - KMManager.executeResourceUpdate(this); + // Checking for updates should never be allowed to crash the keyboard. + // Just silently fail if this occurs. + if(DownloadFileUtils.getDownloadManager(this) != null) { + // Will try to emit a toast if it fails - i.e., is not silent. + KMManager.executeResourceUpdate(this); + } } @Override diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java index 908d2f4538..68db37f57e 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java @@ -32,6 +32,7 @@ import com.keyman.engine.KeyboardEventHandler.OnKeyboardDownloadEventListener; import com.keyman.engine.KeyboardEventHandler.OnKeyboardEventListener; import com.keyman.engine.cloud.CloudApiTypes; import com.keyman.engine.cloud.CloudDownloadMgr; +import com.keyman.engine.cloud.DownloadManagerDisabledException; import com.keyman.engine.cloud.impl.CloudLexicalModelMetaDataDownloadCallback; import com.keyman.engine.data.CloudRepository; import com.keyman.engine.data.Dataset; @@ -154,6 +155,7 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene } KMManager.initialize(getApplicationContext(), KeyboardType.KEYBOARD_TYPE_INAPP); + KMManager.executeResourceUpdate(this); DefaultLanguageResource.install(context); diff --git a/android/KMAPro/kMAPro/src/main/res/values/strings.xml b/android/KMAPro/kMAPro/src/main/res/values/strings.xml index 01546f91ba..d10facf639 100644 --- a/android/KMAPro/kMAPro/src/main/res/values/strings.xml +++ b/android/KMAPro/kMAPro/src/main/res/values/strings.xml @@ -27,6 +27,8 @@ Install Updates + + DownloadManager disabled - cannot check for updates Version: %1$s From 31af7cfc463a75214bcaa84377fb656bbd66e588 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Thu, 13 Feb 2025 13:41:46 +0700 Subject: [PATCH 03/15] chore(android): handle other potentially-affected cases --- .../com/tavultesoft/kmapro/MainActivity.java | 12 ++++++--- .../engine/KMKeyboardDownloaderActivity.java | 27 ++++++++++++++----- .../keyman/engine/ModelPickerActivity.java | 13 ++++++--- ...dLexicalModelMetaDataDownloadCallback.java | 13 ++++++--- 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java index 68db37f57e..00a66575ca 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java @@ -979,9 +979,15 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene aPreparedCloudApiParams.add(new CloudApiTypes.CloudApiParam( CloudApiTypes.ApiTarget.KeyboardLexicalModels, url).setType(CloudApiTypes.JSONType.Array)); - CloudDownloadMgr.getInstance().executeAsDownload( - context, _downloadid, null, _callback, - aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); + try { + CloudDownloadMgr.getInstance().executeAsDownload( + context, _downloadid, null, _callback, + aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); + } catch (DownloadManagerDisabledException e) { + Toast.makeText(context, + context.getString(R.string.update_check_unavailable), + Toast.LENGTH_SHORT).show(); + } } } } diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java index e4f3055763..658596ba6f 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java @@ -10,6 +10,7 @@ import androidx.appcompat.app.AppCompatActivity; import com.keyman.engine.cloud.CloudApiTypes; import com.keyman.engine.cloud.CloudDataJsonUtil; import com.keyman.engine.cloud.CloudDownloadMgr; +import com.keyman.engine.cloud.DownloadManagerDisabledException; import com.keyman.engine.cloud.impl.CloudKeyboardPackageDownloadCallback; import com.keyman.engine.data.Keyboard; import com.keyman.engine.data.KeyboardController; @@ -72,7 +73,7 @@ public class KMKeyboardDownloaderActivity extends BaseActivity { //TODO: move to keyboard manager class private static ArrayList kbDownloadEventListeners = null; - + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -164,9 +165,15 @@ public class KMKeyboardDownloaderActivity extends BaseActivity { context.getString(R.string.keyboard_download_start_in_background), Toast.LENGTH_SHORT).show(); - CloudDownloadMgr.getInstance().executeAsDownload( - context, _downloadid, null, _callback, - aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); + try { + CloudDownloadMgr.getInstance().executeAsDownload( + context, _downloadid, null, _callback, + aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); + } catch (DownloadManagerDisabledException e) { + Toast.makeText(context, + context.getString(R.string.update_check_unavailable), + Toast.LENGTH_SHORT).show(); + } } ((AppCompatActivity) context).finish(); @@ -219,9 +226,15 @@ public class KMKeyboardDownloaderActivity extends BaseActivity { context.getString(R.string.dictionary_download_start_in_background), Toast.LENGTH_SHORT).show(); - CloudDownloadMgr.getInstance().executeAsDownload( - context, _downloadid, null, _callback, - aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); + try { + CloudDownloadMgr.getInstance().executeAsDownload( + context, _downloadid, null, _callback, + aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); + } catch (DownloadManagerDisabledException e) { + Toast.makeText(context, + context.getString(R.string.update_check_unavailable), + Toast.LENGTH_SHORT).show(); + } } ((AppCompatActivity) context).finish(); diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java b/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java index 4595aae1e7..82b854d908 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java @@ -22,6 +22,7 @@ import androidx.appcompat.widget.Toolbar; import com.keyman.engine.cloud.CloudApiTypes; import com.keyman.engine.cloud.CloudDownloadMgr; +import com.keyman.engine.cloud.DownloadManagerDisabledException; import com.keyman.engine.cloud.impl.CloudLexicalModelMetaDataDownloadCallback; import com.keyman.engine.data.CloudRepository; import com.keyman.engine.data.Dataset; @@ -110,9 +111,15 @@ public final class ModelPickerActivity extends BaseActivity { aPreparedCloudApiParams.add(new CloudApiTypes.CloudApiParam( CloudApiTypes.ApiTarget.KeyboardLexicalModels, url).setType(CloudApiTypes.JSONType.Array)); - CloudDownloadMgr.getInstance().executeAsDownload( - context, _downloadid, null, _callback, - aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); + try { + CloudDownloadMgr.getInstance().executeAsDownload( + context, _downloadid, null, _callback, + aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); + } catch (DownloadManagerDisabledException e) { + Toast.makeText(context, + context.getString(R.string.update_check_unavailable), + Toast.LENGTH_SHORT).show(); + } } else { Toast.makeText(context, context.getString(R.string.cannot_connect), diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java index 2063d2d10f..79c0f37e70 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java @@ -12,6 +12,7 @@ import com.keyman.engine.R; import com.keyman.engine.cloud.CloudApiTypes; import com.keyman.engine.cloud.CloudDataJsonUtil; import com.keyman.engine.cloud.CloudDownloadMgr; +import com.keyman.engine.cloud.DownloadManagerDisabledException; import com.keyman.engine.cloud.ICloudDownloadCallback; import com.keyman.engine.util.KMLog; @@ -111,9 +112,15 @@ public class CloudLexicalModelMetaDataDownloadCallback implements ICloudDownload BaseActivity.makeToast(aContext, R.string.dictionary_download_start_in_background, Toast.LENGTH_SHORT); - CloudDownloadMgr.getInstance().executeAsDownload(aContext, - _r.additionalDownloadid, null, _callback, - _r.additionalDownloads.toArray(new CloudApiTypes.CloudApiParam[0])); + try { + CloudDownloadMgr.getInstance().executeAsDownload(aContext, + _r.additionalDownloadid, null, _callback, + _r.additionalDownloads.toArray(new CloudApiTypes.CloudApiParam[0])); + } catch (DownloadManagerDisabledException e) { + Toast.makeText(aContext, + aContext.getString(R.string.update_check_unavailable), + Toast.LENGTH_SHORT).show(); + } } } } From cb9546fdde4691ca05f0a5e290ab7ee3ec1cc1c1 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Thu, 13 Feb 2025 14:08:26 +0700 Subject: [PATCH 04/15] fix(android): extra robustness in cloud-repo-query operation --- .../src/main/java/com/keyman/engine/data/CloudRepository.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/data/CloudRepository.java b/android/KMEA/app/src/main/java/com/keyman/engine/data/CloudRepository.java index 9846dca149..8e1a8bdf35 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/data/CloudRepository.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/data/CloudRepository.java @@ -416,6 +416,9 @@ public class CloudRepository { context, DOWNLOAD_IDENTIFIER_CATALOGUE, memCachedDataset, _download_callback, params); } catch (DownloadManagerDisabledException e) { onFailure.run(); + } catch (Exception e) { + KMLog.LogException(TAG, "Unexpected exception type occurred when trying to query the server", e); + onFailure.run(); } } } From 536201f53e251fa3d91969a37e0f94480e208895 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Thu, 13 Feb 2025 14:37:55 +0700 Subject: [PATCH 05/15] chore(android): add handling + Sentry logging for robustness in case of other sources --- .../com/tavultesoft/kmapro/MainActivity.java | 11 ++++++--- .../engine/KMKeyboardDownloaderActivity.java | 23 ++++++++++++++----- .../keyman/engine/ModelPickerActivity.java | 12 +++++++--- ...dLexicalModelMetaDataDownloadCallback.java | 15 ++++++++---- 4 files changed, 44 insertions(+), 17 deletions(-) diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java index 00a66575ca..138683b505 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java @@ -979,14 +979,19 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene aPreparedCloudApiParams.add(new CloudApiTypes.CloudApiParam( CloudApiTypes.ApiTarget.KeyboardLexicalModels, url).setType(CloudApiTypes.JSONType.Array)); + Toast errorToast = Toast.makeText(context, + context.getString(com.keyman.engine.R.string.update_check_unavailable), + Toast.LENGTH_SHORT); + try { CloudDownloadMgr.getInstance().executeAsDownload( context, _downloadid, null, _callback, aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); } catch (DownloadManagerDisabledException e) { - Toast.makeText(context, - context.getString(R.string.update_check_unavailable), - Toast.LENGTH_SHORT).show(); + errorToast.show(); + } catch (Exception e) { + errorToast.show(); + KMLog.LogException(TAG, "Unexpected exception occurred during download attempt", e); } } } diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java index 658596ba6f..468433fee0 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java @@ -15,6 +15,7 @@ import com.keyman.engine.cloud.impl.CloudKeyboardPackageDownloadCallback; import com.keyman.engine.data.Keyboard; import com.keyman.engine.data.KeyboardController; import com.keyman.engine.cloud.impl.CloudLexicalPackageDownloadCallback; +import com.keyman.engine.util.KMLog; import java.util.ArrayList; import java.util.HashMap; @@ -165,14 +166,19 @@ public class KMKeyboardDownloaderActivity extends BaseActivity { context.getString(R.string.keyboard_download_start_in_background), Toast.LENGTH_SHORT).show(); + Toast errorToast = Toast.makeText(context, + context.getString(R.string.update_check_unavailable), + Toast.LENGTH_SHORT); + try { CloudDownloadMgr.getInstance().executeAsDownload( context, _downloadid, null, _callback, aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); } catch (DownloadManagerDisabledException e) { - Toast.makeText(context, - context.getString(R.string.update_check_unavailable), - Toast.LENGTH_SHORT).show(); + errorToast.show(); + } catch (Exception e) { + errorToast.show(); + KMLog.LogException(TAG, "Unexpected exception occurred during download attempt", e); } } @@ -226,14 +232,19 @@ public class KMKeyboardDownloaderActivity extends BaseActivity { context.getString(R.string.dictionary_download_start_in_background), Toast.LENGTH_SHORT).show(); + Toast errorToast = Toast.makeText(context, + context.getString(R.string.update_check_unavailable), + Toast.LENGTH_SHORT); + try { CloudDownloadMgr.getInstance().executeAsDownload( context, _downloadid, null, _callback, aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); } catch (DownloadManagerDisabledException e) { - Toast.makeText(context, - context.getString(R.string.update_check_unavailable), - Toast.LENGTH_SHORT).show(); + errorToast.show(); + } catch (Exception e) { + errorToast.show(); + KMLog.LogException(TAG, "Unexpected exception occurred during download attempt", e); } } diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java b/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java index 82b854d908..23b6a9125a 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java @@ -29,6 +29,7 @@ import com.keyman.engine.data.Dataset; import com.keyman.engine.data.LexicalModel; import com.keyman.engine.data.adapters.NestedAdapter; import com.keyman.engine.util.BCP47; +import com.keyman.engine.util.KMLog; import com.keyman.engine.util.MapCompat; import java.io.File; @@ -111,14 +112,19 @@ public final class ModelPickerActivity extends BaseActivity { aPreparedCloudApiParams.add(new CloudApiTypes.CloudApiParam( CloudApiTypes.ApiTarget.KeyboardLexicalModels, url).setType(CloudApiTypes.JSONType.Array)); + Toast errorToast = Toast.makeText(context, + context.getString(R.string.update_check_unavailable), + Toast.LENGTH_SHORT); + try { CloudDownloadMgr.getInstance().executeAsDownload( context, _downloadid, null, _callback, aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); } catch (DownloadManagerDisabledException e) { - Toast.makeText(context, - context.getString(R.string.update_check_unavailable), - Toast.LENGTH_SHORT).show(); + errorToast.show(); + } catch (Exception e) { + errorToast.show(); + KMLog.LogException(TAG, "Unexpected exception occurred during download attempt", e); } } else { Toast.makeText(context, diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java index 79c0f37e70..d5cb08547b 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java @@ -112,14 +112,19 @@ public class CloudLexicalModelMetaDataDownloadCallback implements ICloudDownload BaseActivity.makeToast(aContext, R.string.dictionary_download_start_in_background, Toast.LENGTH_SHORT); + Toast errorToast = Toast.makeText(aContext, + aContext.getString(R.string.update_check_unavailable), + Toast.LENGTH_SHORT); + try { - CloudDownloadMgr.getInstance().executeAsDownload(aContext, - _r.additionalDownloadid, null, _callback, + CloudDownloadMgr.getInstance().executeAsDownload( + aContext, _r.additionalDownloadid, null, _callback, _r.additionalDownloads.toArray(new CloudApiTypes.CloudApiParam[0])); } catch (DownloadManagerDisabledException e) { - Toast.makeText(aContext, - aContext.getString(R.string.update_check_unavailable), - Toast.LENGTH_SHORT).show(); + errorToast.show(); + } catch (Exception e) { + errorToast.show(); + KMLog.LogException(TAG, "Unexpected exception occurred during download attempt", e); } } } From 18d4af5ac12ffc1df9adaecafbd2e63b3e0223df Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Thu, 13 Feb 2025 14:49:51 +0700 Subject: [PATCH 06/15] chore(android): clean up unused string dev artifact --- android/KMAPro/kMAPro/src/main/res/values/strings.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/android/KMAPro/kMAPro/src/main/res/values/strings.xml b/android/KMAPro/kMAPro/src/main/res/values/strings.xml index d10facf639..a0447d0f89 100644 --- a/android/KMAPro/kMAPro/src/main/res/values/strings.xml +++ b/android/KMAPro/kMAPro/src/main/res/values/strings.xml @@ -27,9 +27,6 @@ Install Updates - - DownloadManager disabled - cannot check for updates - Version: %1$s From 8ef559e90e244ee7c02547a3138fb89b2070fc24 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Thu, 13 Feb 2025 14:54:34 +0700 Subject: [PATCH 07/15] docs(android): improve doc for new function --- .../java/com/keyman/engine/util/DownloadFileUtils.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/util/DownloadFileUtils.java b/android/KMEA/app/src/main/java/com/keyman/engine/util/DownloadFileUtils.java index 96ee696074..d9315f3f4d 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/util/DownloadFileUtils.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/util/DownloadFileUtils.java @@ -26,12 +26,14 @@ public final class DownloadFileUtils { private static final String DOWNLOAD_MANAGER_PACKAGE_NAME = "com.android.providers.downloads"; /** - * Determines whether or not Android's `DownloadManager` service is active; - * downloads and cloud queries are impossible when it's disabled. + * Determines whether or not Android's `DownloadManager` service is active, only + * returning an instance of DownloadManager when it is currently accessible and enabled. + * Downloads and cloud queries are impossible when it's disabled. * * This solution is based heavily on * https://gist.github.com/Folyd/b9412bb6e2b06eb511f7. - * @return + * @return A valid and enabled reference to the system's DownloadManager service. May be null + * if it is not accessible or is disabled. */ public static DownloadManager getDownloadManager(Context context) { DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); From 8910531c10d58329ceed6fbd4dd30c2810b81d17 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Fri, 14 Feb 2025 09:09:10 +0700 Subject: [PATCH 08/15] refactor(android): deduplicate handler setup into helper method --- .../com/tavultesoft/kmapro/MainActivity.java | 17 ++-------- .../engine/KMKeyboardDownloaderActivity.java | 34 ++++--------------- .../keyman/engine/ModelPickerActivity.java | 17 ++-------- .../keyman/engine/cloud/CloudDownloadMgr.java | 23 +++++++++++-- ...dLexicalModelMetaDataDownloadCallback.java | 17 ++-------- 5 files changed, 36 insertions(+), 72 deletions(-) diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java index 138683b505..68db37f57e 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java @@ -979,20 +979,9 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene aPreparedCloudApiParams.add(new CloudApiTypes.CloudApiParam( CloudApiTypes.ApiTarget.KeyboardLexicalModels, url).setType(CloudApiTypes.JSONType.Array)); - Toast errorToast = Toast.makeText(context, - context.getString(com.keyman.engine.R.string.update_check_unavailable), - Toast.LENGTH_SHORT); - - try { - CloudDownloadMgr.getInstance().executeAsDownload( - context, _downloadid, null, _callback, - aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); - } catch (DownloadManagerDisabledException e) { - errorToast.show(); - } catch (Exception e) { - errorToast.show(); - KMLog.LogException(TAG, "Unexpected exception occurred during download attempt", e); - } + CloudDownloadMgr.getInstance().executeAsDownload( + context, _downloadid, null, _callback, + aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); } } } diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java index 468433fee0..ac875eec0f 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java @@ -166,20 +166,9 @@ public class KMKeyboardDownloaderActivity extends BaseActivity { context.getString(R.string.keyboard_download_start_in_background), Toast.LENGTH_SHORT).show(); - Toast errorToast = Toast.makeText(context, - context.getString(R.string.update_check_unavailable), - Toast.LENGTH_SHORT); - - try { - CloudDownloadMgr.getInstance().executeAsDownload( - context, _downloadid, null, _callback, - aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); - } catch (DownloadManagerDisabledException e) { - errorToast.show(); - } catch (Exception e) { - errorToast.show(); - KMLog.LogException(TAG, "Unexpected exception occurred during download attempt", e); - } + CloudDownloadMgr.getInstance().executeAsDownload( + context, _downloadid, null, _callback, + aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); } ((AppCompatActivity) context).finish(); @@ -232,20 +221,9 @@ public class KMKeyboardDownloaderActivity extends BaseActivity { context.getString(R.string.dictionary_download_start_in_background), Toast.LENGTH_SHORT).show(); - Toast errorToast = Toast.makeText(context, - context.getString(R.string.update_check_unavailable), - Toast.LENGTH_SHORT); - - try { - CloudDownloadMgr.getInstance().executeAsDownload( - context, _downloadid, null, _callback, - aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); - } catch (DownloadManagerDisabledException e) { - errorToast.show(); - } catch (Exception e) { - errorToast.show(); - KMLog.LogException(TAG, "Unexpected exception occurred during download attempt", e); - } + CloudDownloadMgr.getInstance().executeAsDownload( + context, _downloadid, null, _callback, + aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); } ((AppCompatActivity) context).finish(); diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java b/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java index 23b6a9125a..3c1a21f6d9 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java @@ -112,20 +112,9 @@ public final class ModelPickerActivity extends BaseActivity { aPreparedCloudApiParams.add(new CloudApiTypes.CloudApiParam( CloudApiTypes.ApiTarget.KeyboardLexicalModels, url).setType(CloudApiTypes.JSONType.Array)); - Toast errorToast = Toast.makeText(context, - context.getString(R.string.update_check_unavailable), - Toast.LENGTH_SHORT); - - try { - CloudDownloadMgr.getInstance().executeAsDownload( - context, _downloadid, null, _callback, - aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); - } catch (DownloadManagerDisabledException e) { - errorToast.show(); - } catch (Exception e) { - errorToast.show(); - KMLog.LogException(TAG, "Unexpected exception occurred during download attempt", e); - } + CloudDownloadMgr.getInstance().executeAsDownload( + context, _downloadid, null, _callback, + aPreparedCloudApiParams.toArray(new CloudApiTypes.CloudApiParam[0])); } else { Toast.makeText(context, context.getString(R.string.cannot_connect), diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java index edf067221e..adc84b9960 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java @@ -9,6 +9,7 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.util.Log; +import android.widget.Toast; import com.keyman.engine.util.KMLog; import com.keyman.engine.util.DownloadFileUtils; @@ -212,8 +213,26 @@ public class CloudDownloadMgr{ public void executeAsDownload(Context aContext, String aDownloadIdentifier, ModelType aTargetModel, ICloudDownloadCallback aCallback, - CloudApiTypes.CloudApiParam... params) throws DownloadManagerDisabledException - { + CloudApiTypes.CloudApiParam... params) throws DownloadManagerDisabledException { + Toast errorToast = Toast.makeText(aContext, + // TODO: reintroduce custom error message + aContext.getString(com.keyman.engine.R.string.update_check_unavailable), + Toast.LENGTH_SHORT); + + try { + executeAsDownloadInternal(aContext, aDownloadIdentifier, aTargetModel, aCallback, params); + } catch (DownloadManagerDisabledException e) { + errorToast.show(); + } catch (Exception e) { + errorToast.show(); + KMLog.LogException(TAG, "Unexpected exception occurred during download/query attempt", e); + } + } + + private void executeAsDownloadInternal(Context aContext, String aDownloadIdentifier, + ModelType aTargetModel, + ICloudDownloadCallback aCallback, + CloudApiTypes.CloudApiParam... params) throws DownloadManagerDisabledException { if(!isInitialized) { Log.w(TAG, "DownloadManager not initialized. Initializing CloudDownloadMgr."); initialize(aContext); diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java index d5cb08547b..daeaebf7b8 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java @@ -112,20 +112,9 @@ public class CloudLexicalModelMetaDataDownloadCallback implements ICloudDownload BaseActivity.makeToast(aContext, R.string.dictionary_download_start_in_background, Toast.LENGTH_SHORT); - Toast errorToast = Toast.makeText(aContext, - aContext.getString(R.string.update_check_unavailable), - Toast.LENGTH_SHORT); - - try { - CloudDownloadMgr.getInstance().executeAsDownload( - aContext, _r.additionalDownloadid, null, _callback, - _r.additionalDownloads.toArray(new CloudApiTypes.CloudApiParam[0])); - } catch (DownloadManagerDisabledException e) { - errorToast.show(); - } catch (Exception e) { - errorToast.show(); - KMLog.LogException(TAG, "Unexpected exception occurred during download attempt", e); - } + CloudDownloadMgr.getInstance().executeAsDownload( + aContext, _r.additionalDownloadid, null, _callback, + _r.additionalDownloads.toArray(new CloudApiTypes.CloudApiParam[0])); } } } From 0058edb3dd36146b5c6e6deb2bd547a0d74b8621 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Fri, 14 Feb 2025 09:18:57 +0700 Subject: [PATCH 09/15] change(android): reintroduce specialized notification --- .../com/keyman/engine/cloud/CloudDownloadMgr.java | 14 +++++++------- android/KMEA/app/src/main/res/values/strings.xml | 3 +++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java index adc84b9960..07023d65a7 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java @@ -11,6 +11,7 @@ import android.os.Build; import android.util.Log; import android.widget.Toast; +import com.keyman.engine.R; import com.keyman.engine.util.KMLog; import com.keyman.engine.util.DownloadFileUtils; @@ -214,17 +215,16 @@ public class CloudDownloadMgr{ ModelType aTargetModel, ICloudDownloadCallback aCallback, CloudApiTypes.CloudApiParam... params) throws DownloadManagerDisabledException { - Toast errorToast = Toast.makeText(aContext, - // TODO: reintroduce custom error message - aContext.getString(com.keyman.engine.R.string.update_check_unavailable), - Toast.LENGTH_SHORT); - try { executeAsDownloadInternal(aContext, aDownloadIdentifier, aTargetModel, aCallback, params); } catch (DownloadManagerDisabledException e) { - errorToast.show(); + Toast.makeText(aContext, + aContext.getString(R.string.update_check_download_manager_disabled), + Toast.LENGTH_SHORT).show(); } catch (Exception e) { - errorToast.show(); + Toast.makeText(aContext, + aContext.getString(R.string.update_check_unavailable), + Toast.LENGTH_SHORT).show(); KMLog.LogException(TAG, "Unexpected exception occurred during download/query attempt", e); } } diff --git a/android/KMEA/app/src/main/res/values/strings.xml b/android/KMEA/app/src/main/res/values/strings.xml index 5c5d6f3173..e477f36574 100644 --- a/android/KMEA/app/src/main/res/values/strings.xml +++ b/android/KMEA/app/src/main/res/values/strings.xml @@ -184,6 +184,9 @@ Failed to access server! + + DownloadManager disabled - cannot check for updates + "All resources are up to date!" From 81e84b6147f7a672db0bfc249b9bd7ca14aed270 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Mon, 17 Feb 2025 09:28:17 +0700 Subject: [PATCH 10/15] chore(android): remove unused imports --- .../kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java | 1 - .../src/main/java/com/tavultesoft/kmapro/MainActivity.java | 1 - .../java/com/keyman/engine/KMKeyboardDownloaderActivity.java | 1 - .../app/src/main/java/com/keyman/engine/ModelPickerActivity.java | 1 - .../cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java | 1 - 5 files changed, 5 deletions(-) diff --git a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java index 690f36d9ed..7528f9589d 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java +++ b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java @@ -4,7 +4,6 @@ package com.keyman.android; -import com.keyman.engine.cloud.DownloadManagerDisabledException; import com.keyman.engine.util.DownloadFileUtils; import com.keyman.engine.util.KMLog; import com.tavultesoft.kmapro.AdjustLongpressDelayActivity; diff --git a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java index 68db37f57e..9f938a722f 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java +++ b/android/KMAPro/kMAPro/src/main/java/com/tavultesoft/kmapro/MainActivity.java @@ -32,7 +32,6 @@ import com.keyman.engine.KeyboardEventHandler.OnKeyboardDownloadEventListener; import com.keyman.engine.KeyboardEventHandler.OnKeyboardEventListener; import com.keyman.engine.cloud.CloudApiTypes; import com.keyman.engine.cloud.CloudDownloadMgr; -import com.keyman.engine.cloud.DownloadManagerDisabledException; import com.keyman.engine.cloud.impl.CloudLexicalModelMetaDataDownloadCallback; import com.keyman.engine.data.CloudRepository; import com.keyman.engine.data.Dataset; diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java index ac875eec0f..66d81457dd 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java @@ -10,7 +10,6 @@ import androidx.appcompat.app.AppCompatActivity; import com.keyman.engine.cloud.CloudApiTypes; import com.keyman.engine.cloud.CloudDataJsonUtil; import com.keyman.engine.cloud.CloudDownloadMgr; -import com.keyman.engine.cloud.DownloadManagerDisabledException; import com.keyman.engine.cloud.impl.CloudKeyboardPackageDownloadCallback; import com.keyman.engine.data.Keyboard; import com.keyman.engine.data.KeyboardController; diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java b/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java index 3c1a21f6d9..9e8c4088cb 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java @@ -22,7 +22,6 @@ import androidx.appcompat.widget.Toolbar; import com.keyman.engine.cloud.CloudApiTypes; import com.keyman.engine.cloud.CloudDownloadMgr; -import com.keyman.engine.cloud.DownloadManagerDisabledException; import com.keyman.engine.cloud.impl.CloudLexicalModelMetaDataDownloadCallback; import com.keyman.engine.data.CloudRepository; import com.keyman.engine.data.Dataset; diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java index daeaebf7b8..62f65a96ed 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/impl/CloudLexicalModelMetaDataDownloadCallback.java @@ -12,7 +12,6 @@ import com.keyman.engine.R; import com.keyman.engine.cloud.CloudApiTypes; import com.keyman.engine.cloud.CloudDataJsonUtil; import com.keyman.engine.cloud.CloudDownloadMgr; -import com.keyman.engine.cloud.DownloadManagerDisabledException; import com.keyman.engine.cloud.ICloudDownloadCallback; import com.keyman.engine.util.KMLog; From c2434c45aa326c548b9906a9661538b29320e92f Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Mon, 17 Feb 2025 09:42:00 +0700 Subject: [PATCH 11/15] change(android): drop no-op line, unneeded exception handler --- .../com/keyman/engine/cloud/CloudDownloadMgr.java | 7 +++---- .../java/com/keyman/engine/data/CloudRepository.java | 12 +++--------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java index 07023d65a7..4d78041d99 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java @@ -242,10 +242,9 @@ public class CloudDownloadMgr{ DownloadManager downloadManager = DownloadFileUtils.getDownloadManager(aContext); if(downloadManager == null) { - aCallback.initializeContext(aContext); - - // Signal the failure state; we literally can't do this right now! - // Fortunately, that does mean there's no need to signal via callback. + // The callback object provided to us provides no way to directly signal a + // failure. That said, we can also immediately detect that we WILL fail and + // corresponding error _now_, rather than later. // // Unique custom error so it's easy to explicitly filter. throw new DownloadManagerDisabledException(); diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/data/CloudRepository.java b/android/KMEA/app/src/main/java/com/keyman/engine/data/CloudRepository.java index 8e1a8bdf35..f9cee7b5c0 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/data/CloudRepository.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/data/CloudRepository.java @@ -411,15 +411,9 @@ public class CloudRepository { BaseActivity.makeToast(context, R.string.catalog_download_is_running_in_background, Toast.LENGTH_SHORT); } else { updateIsRunning = true; - try { - CloudDownloadMgr.getInstance().executeAsDownload( - context, DOWNLOAD_IDENTIFIER_CATALOGUE, memCachedDataset, _download_callback, params); - } catch (DownloadManagerDisabledException e) { - onFailure.run(); - } catch (Exception e) { - KMLog.LogException(TAG, "Unexpected exception type occurred when trying to query the server", e); - onFailure.run(); - } + CloudDownloadMgr.getInstance().executeAsDownload( + context, DOWNLOAD_IDENTIFIER_CATALOGUE, memCachedDataset, _download_callback, params); + // if it fails, we should `updateIsRunning = false`, right? } } } From 1c2c845b4cb721cd96ce8a05f2e90c6e5713c292 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Mon, 17 Feb 2025 09:43:30 +0700 Subject: [PATCH 12/15] chore(android): drop unreferenced KMLog import --- .../java/com/keyman/engine/KMKeyboardDownloaderActivity.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java index 66d81457dd..7bca1e00fc 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java @@ -13,8 +13,7 @@ import com.keyman.engine.cloud.CloudDownloadMgr; import com.keyman.engine.cloud.impl.CloudKeyboardPackageDownloadCallback; import com.keyman.engine.data.Keyboard; import com.keyman.engine.data.KeyboardController; -import com.keyman.engine.cloud.impl.CloudLexicalPackageDownloadCallback; -import com.keyman.engine.util.KMLog; +import com.keyman.engine.cloud.impl.CloudLexicalPackageDownloadCallback;= import java.util.ArrayList; import java.util.HashMap; From 2437ff5cc26beace5ba6fab8629138779a2a721f Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Mon, 17 Feb 2025 09:45:05 +0700 Subject: [PATCH 13/15] chore(android): and another, plus removal of accidental typo --- .../kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java | 1 - .../java/com/keyman/engine/KMKeyboardDownloaderActivity.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java index 7528f9589d..d5a4aea69f 100644 --- a/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java +++ b/android/KMAPro/kMAPro/src/main/java/com/keyman/android/SystemKeyboard.java @@ -5,7 +5,6 @@ package com.keyman.android; import com.keyman.engine.util.DownloadFileUtils; -import com.keyman.engine.util.KMLog; import com.tavultesoft.kmapro.AdjustLongpressDelayActivity; import com.tavultesoft.kmapro.BuildConfig; import com.tavultesoft.kmapro.DefaultLanguageResource; diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java index 7bca1e00fc..3f96c18289 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/KMKeyboardDownloaderActivity.java @@ -13,7 +13,7 @@ import com.keyman.engine.cloud.CloudDownloadMgr; import com.keyman.engine.cloud.impl.CloudKeyboardPackageDownloadCallback; import com.keyman.engine.data.Keyboard; import com.keyman.engine.data.KeyboardController; -import com.keyman.engine.cloud.impl.CloudLexicalPackageDownloadCallback;= +import com.keyman.engine.cloud.impl.CloudLexicalPackageDownloadCallback; import java.util.ArrayList; import java.util.HashMap; From 252a31ada2480cd24b339715978bfbd025633373 Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Mon, 17 Feb 2025 09:47:28 +0700 Subject: [PATCH 14/15] chore(android): removing each KMLog one at a time, apparently --- .../app/src/main/java/com/keyman/engine/ModelPickerActivity.java | 1 - 1 file changed, 1 deletion(-) diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java b/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java index 9e8c4088cb..4595aae1e7 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/ModelPickerActivity.java @@ -28,7 +28,6 @@ import com.keyman.engine.data.Dataset; import com.keyman.engine.data.LexicalModel; import com.keyman.engine.data.adapters.NestedAdapter; import com.keyman.engine.util.BCP47; -import com.keyman.engine.util.KMLog; import com.keyman.engine.util.MapCompat; import java.io.File; From cdafdd70a3e820036186b2a6888fe1d5dfde3ace Mon Sep 17 00:00:00 2001 From: Joshua Horton Date: Mon, 17 Feb 2025 10:26:18 +0700 Subject: [PATCH 15/15] change(android): adds boolean return to executeAsDownload --- .../java/com/keyman/engine/cloud/CloudDownloadMgr.java | 9 +++++++-- .../java/com/keyman/engine/data/CloudRepository.java | 8 ++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java index 4d78041d99..bb8ee190b6 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/cloud/CloudDownloadMgr.java @@ -210,23 +210,28 @@ public class CloudDownloadMgr{ * @param params the cloud api params for download * @param the target models type * @param the cloud requests result type + * @return `false` if the download request cannot be executed; `true` otherwise. */ - public void executeAsDownload(Context aContext, String aDownloadIdentifier, + public boolean executeAsDownload(Context aContext, String aDownloadIdentifier, ModelType aTargetModel, ICloudDownloadCallback aCallback, - CloudApiTypes.CloudApiParam... params) throws DownloadManagerDisabledException { + CloudApiTypes.CloudApiParam... params) { try { executeAsDownloadInternal(aContext, aDownloadIdentifier, aTargetModel, aCallback, params); } catch (DownloadManagerDisabledException e) { Toast.makeText(aContext, aContext.getString(R.string.update_check_download_manager_disabled), Toast.LENGTH_SHORT).show(); + return false; } catch (Exception e) { Toast.makeText(aContext, aContext.getString(R.string.update_check_unavailable), Toast.LENGTH_SHORT).show(); KMLog.LogException(TAG, "Unexpected exception occurred during download/query attempt", e); + return false; } + + return true; } private void executeAsDownloadInternal(Context aContext, String aDownloadIdentifier, diff --git a/android/KMEA/app/src/main/java/com/keyman/engine/data/CloudRepository.java b/android/KMEA/app/src/main/java/com/keyman/engine/data/CloudRepository.java index f9cee7b5c0..4676d59c77 100644 --- a/android/KMEA/app/src/main/java/com/keyman/engine/data/CloudRepository.java +++ b/android/KMEA/app/src/main/java/com/keyman/engine/data/CloudRepository.java @@ -411,9 +411,13 @@ public class CloudRepository { BaseActivity.makeToast(context, R.string.catalog_download_is_running_in_background, Toast.LENGTH_SHORT); } else { updateIsRunning = true; - CloudDownloadMgr.getInstance().executeAsDownload( + boolean executionStarted = CloudDownloadMgr.getInstance().executeAsDownload( context, DOWNLOAD_IDENTIFIER_CATALOGUE, memCachedDataset, _download_callback, params); - // if it fails, we should `updateIsRunning = false`, right? + if(!executionStarted) { + // Since we couldn't initiate the execution's async components, + // we need to immediately clear the update-running flag. + updateIsRunning = false; + } } } }