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