diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 72898c755..8e9f9f85d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -221,6 +221,14 @@
android:name="com.kunzisoft.keepass.services.KeyboardEntryNotificationService"
android:enabled="true"
android:exported="false" />
+
+
+
+
+
+
diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt
index d25b93964..5c0d740c1 100644
--- a/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt
+++ b/app/src/main/java/com/kunzisoft/keepass/activities/FileDatabaseSelectActivity.kt
@@ -88,6 +88,12 @@ class FileDatabaseSelectActivity : DatabaseModeActivity(),
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ // Enabling/disabling MagikeyboardService is normally done by DexModeReceiver, but this
+ // additional check will allow the keyboard to be reenabled more easily if the app crashes
+ // or is force quit within DeX mode and then the user leaves DeX mode. Without this, the
+ // user would need to enter and exit DeX mode once to reenable the service.
+ MagikeyboardUtil.setEnabled(this, !DexUtil.isDexMode(resources.configuration))
+
mFileDatabaseHistoryAction = FileDatabaseHistoryAction.getInstance(applicationContext)
setContentView(R.layout.activity_file_selection)
diff --git a/app/src/main/java/com/kunzisoft/keepass/receivers/DexModeReceiver.kt b/app/src/main/java/com/kunzisoft/keepass/receivers/DexModeReceiver.kt
new file mode 100644
index 000000000..7844c4251
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/receivers/DexModeReceiver.kt
@@ -0,0 +1,33 @@
+package com.kunzisoft.keepass.receivers
+
+import android.content.BroadcastReceiver
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.util.Log
+import com.kunzisoft.keepass.magikeyboard.MagikeyboardService
+import com.kunzisoft.keepass.utils.DexUtil
+import com.kunzisoft.keepass.utils.MagikeyboardUtil
+
+class DexModeReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ val enabled = when (intent?.action) {
+ "android.app.action.ENTER_KNOX_DESKTOP_MODE" -> {
+ Log.i(TAG, "Entered DeX mode")
+ false
+ }
+ "android.app.action.EXIT_KNOX_DESKTOP_MODE" -> {
+ Log.i(TAG, "Left DeX mode")
+ true
+ }
+ else -> return
+ }
+
+ MagikeyboardUtil.setEnabled(context!!, enabled)
+ }
+
+ companion object {
+ private val TAG = DexModeReceiver::class.java.name
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/DexUtil.kt b/app/src/main/java/com/kunzisoft/keepass/utils/DexUtil.kt
new file mode 100644
index 000000000..18a62301d
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/utils/DexUtil.kt
@@ -0,0 +1,27 @@
+package com.kunzisoft.keepass.utils
+
+import android.content.res.Configuration
+import android.util.Log
+
+object DexUtil {
+ private val TAG = DexUtil::class.java.name
+
+ // Determine if the current environment is in DeX mode. Always returns false on non-Samsung
+ // devices.
+ fun isDexMode(config: Configuration): Boolean {
+ // This is the documented way to check this: https://developer.samsung.com/samsung-dex/modify-optimizing.html
+ return try {
+ val configClass = config.javaClass
+ val enabledConstant = configClass.getField("SEM_DESKTOP_MODE_ENABLED").getInt(configClass)
+ val enabledField = configClass.getField("semDesktopModeEnabled").getInt(config)
+ val isEnabled = enabledConstant == enabledField
+
+ Log.d(TAG, "DeX currently enabled: $isEnabled")
+
+ isEnabled
+ } catch (e: Exception) {
+ Log.d(TAG, "Failed to check for DeX mode; likely not Samsung device: $e")
+ false
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/kunzisoft/keepass/utils/MagikeyboardUtil.kt b/app/src/main/java/com/kunzisoft/keepass/utils/MagikeyboardUtil.kt
new file mode 100644
index 000000000..be095f648
--- /dev/null
+++ b/app/src/main/java/com/kunzisoft/keepass/utils/MagikeyboardUtil.kt
@@ -0,0 +1,27 @@
+package com.kunzisoft.keepass.utils
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageManager
+import android.util.Log
+import com.kunzisoft.keepass.magikeyboard.MagikeyboardService
+
+object MagikeyboardUtil {
+ private val TAG = MagikeyboardUtil::class.java.name
+
+ // Set whether MagikeyboardService is enabled. This change is persistent and survives app
+ // crashes and device restarts. The state is changed immediately and does not require an app
+ // restart.
+ fun setEnabled(context: Context, enabled: Boolean) {
+ val componentState = if (enabled) {
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+ } else {
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+ }
+
+ Log.d(TAG, "Setting service state: $enabled")
+
+ val component = ComponentName(context, MagikeyboardService::class.java)
+ context.packageManager.setComponentEnabledSetting(component, componentState, PackageManager.DONT_KILL_APP)
+ }
+}
\ No newline at end of file