diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d74d55e0eb..424e398db2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,7 +18,6 @@ @@ -67,4 +67,4 @@ - \ No newline at end of file + diff --git a/app/src/main/java/io/github/sds100/keymapper/system/accessibility/MyAccessibilityService.kt b/app/src/main/java/io/github/sds100/keymapper/system/accessibility/MyAccessibilityService.kt index bc47591aae..1fdae9a795 100644 --- a/app/src/main/java/io/github/sds100/keymapper/system/accessibility/MyAccessibilityService.kt +++ b/app/src/main/java/io/github/sds100/keymapper/system/accessibility/MyAccessibilityService.kt @@ -1,6 +1,10 @@ package io.github.sds100.keymapper.system.accessibility import android.content.Intent +import android.os.UserManager +import android.view.KeyEvent +import android.view.accessibility.AccessibilityEvent +import androidx.core.content.getSystemService import dagger.hilt.android.AndroidEntryPoint import io.github.sds100.keymapper.base.system.accessibility.BaseAccessibilityService import io.github.sds100.keymapper.base.system.accessibility.BaseAccessibilityServiceController @@ -14,6 +18,9 @@ class MyAccessibilityService : BaseAccessibilityService() { lateinit var controllerFactory: AccessibilityServiceController.Factory private var controller: AccessibilityServiceController? = null + private var loggedLockedInitDelay = false + + private val userManager: UserManager? by lazy { getSystemService() } override fun getController(): BaseAccessibilityServiceController? { return controller @@ -22,15 +29,47 @@ class MyAccessibilityService : BaseAccessibilityService() { override fun onServiceConnected() { super.onServiceConnected() + initializeControllerIfUserUnlocked() + } + + override fun onAccessibilityEvent(event: AccessibilityEvent?) { + if (!initializeControllerIfUserUnlocked()) { + return + } + + super.onAccessibilityEvent(event) + } + + override fun onKeyEvent(event: KeyEvent?): Boolean { + if (!initializeControllerIfUserUnlocked()) { + return false + } + + return super.onKeyEvent(event) + } + + private fun initializeControllerIfUserUnlocked(): Boolean { + if (userManager?.isUserUnlocked == false) { + if (!loggedLockedInitDelay) { + Timber.i("Accessibility service: Delay init because locked.") + loggedLockedInitDelay = true + } + + return false + } + + loggedLockedInitDelay = false + /* I would put this in onCreate but for some reason on some devices getting the application context would return null */ if (controller == null) { controller = controllerFactory.create(this) + controller?.onServiceConnected() } - controller?.onServiceConnected() + return true } override fun onUnbind(intent: Intent?): Boolean { diff --git a/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt b/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt index ee9e54e816..4bf021c4fd 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt @@ -36,6 +36,7 @@ import io.github.sds100.keymapper.system.permissions.AndroidPermissionAdapter import io.github.sds100.keymapper.system.permissions.Permission import java.util.Calendar import javax.inject.Inject +import dagger.Lazy import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.first @@ -51,46 +52,46 @@ abstract class BaseKeyMapperApp : MultiDexApplication() { private val tag = BaseKeyMapperApp::class.simpleName @Inject - lateinit var appCoroutineScope: CoroutineScope + lateinit var appCoroutineScope: Lazy @Inject - lateinit var notificationController: NotificationController + lateinit var notificationController: Lazy @Inject - lateinit var packageManagerAdapter: AndroidPackageManagerAdapter + lateinit var packageManagerAdapter: Lazy @Inject - lateinit var devicesAdapter: AndroidDevicesAdapter + lateinit var devicesAdapter: Lazy @Inject - lateinit var permissionAdapter: AndroidPermissionAdapter + lateinit var permissionAdapter: Lazy @Inject - lateinit var accessibilityServiceAdapter: AccessibilityServiceAdapterImpl + lateinit var accessibilityServiceAdapter: Lazy @Inject - lateinit var autoGrantPermissionController: AutoGrantPermissionController + lateinit var autoGrantPermissionController: Lazy @Inject - lateinit var loggingTree: KeyMapperLoggingTree + lateinit var loggingTree: Lazy @Inject - lateinit var settingsRepository: PreferenceRepositoryImpl + lateinit var settingsRepository: Lazy @Inject - lateinit var logRepository: LogRepository + lateinit var logRepository: Lazy @Inject - lateinit var keyEventRelayServiceWrapper: KeyEventRelayServiceWrapperImpl + lateinit var keyEventRelayServiceWrapper: Lazy @Inject - lateinit var systemBridgeAutoStarter: SystemBridgeAutoStarter + lateinit var systemBridgeAutoStarter: Lazy @Inject - lateinit var systemBridgeConnectionManager: SystemBridgeConnectionManagerImpl + lateinit var systemBridgeConnectionManager: Lazy @Inject - lateinit var systemBridgeLogger: SystemBridgeLogger + lateinit var systemBridgeLogger: Lazy private val processLifecycleOwner by lazy { ProcessLifecycleOwner.get() } @@ -118,16 +119,18 @@ abstract class BaseKeyMapperApp : MultiDexApplication() { Log.i(tag, "KeyMapperApp: OnCreate") Thread.setDefaultUncaughtExceptionHandler { thread, exception -> - // log in a blocking manner and always log regardless of whether the setting is turned on - val entry = LogEntryEntity( - id = 0, - time = Calendar.getInstance().timeInMillis, - severity = LogEntryEntity.SEVERITY_ERROR, - message = exception.stackTraceToString(), - ) - - runBlocking { - logRepository.insertSuspend(entry) + if (userManager?.isUserUnlocked != false) { + // log in a blocking manner and always log regardless of whether the setting is turned on + val entry = LogEntryEntity( + id = 0, + time = Calendar.getInstance().timeInMillis, + severity = LogEntryEntity.SEVERITY_ERROR, + message = exception.stackTraceToString(), + ) + + runBlocking { + logRepository.get().insertSuspend(entry) + } } priorExceptionHandler?.uncaughtException(thread, exception) @@ -168,7 +171,7 @@ abstract class BaseKeyMapperApp : MultiDexApplication() { registerReceiver(broadcastReceiver, intentFilter) - settingsRepository.get(Keys.darkTheme) + settingsRepository.get().get(Keys.darkTheme) .map { it?.toIntOrNull() } .map { when (it) { @@ -178,15 +181,15 @@ abstract class BaseKeyMapperApp : MultiDexApplication() { } } .onEach { mode -> AppCompatDelegate.setDefaultNightMode(mode) } - .launchIn(appCoroutineScope) + .launchIn(appCoroutineScope.get()) if (BuildConfig.BUILD_TYPE == "debug" || BuildConfig.BUILD_TYPE == "debug_release") { Timber.plant(Timber.DebugTree()) } - Timber.plant(loggingTree) + Timber.plant(loggingTree.get()) - notificationController.init() + notificationController.get().init() processLifecycleOwner.lifecycle.addObserver( object : LifecycleObserver { @@ -194,19 +197,19 @@ abstract class BaseKeyMapperApp : MultiDexApplication() { @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onResume() { // when the user returns to the app let everything know that the permissions could have changed - notificationController.onOpenApp() + notificationController.get().onOpenApp() if (BuildConfig.DEBUG && - permissionAdapter.isGranted(Permission.WRITE_SECURE_SETTINGS) + permissionAdapter.get().isGranted(Permission.WRITE_SECURE_SETTINGS) ) { - accessibilityServiceAdapter.start() + accessibilityServiceAdapter.get().start() } } }, ) - appCoroutineScope.launch { - notificationController.openApp.collectLatest { intentAction -> + appCoroutineScope.get().launch { + notificationController.get().openApp.collectLatest { intentAction -> Intent(this@BaseKeyMapperApp, getMainActivityClass()).apply { action = intentAction flags = Intent.FLAG_ACTIVITY_NEW_TASK @@ -216,38 +219,38 @@ abstract class BaseKeyMapperApp : MultiDexApplication() { } } - notificationController.showToast.onEach { toast -> + notificationController.get().showToast.onEach { toast -> Toast.makeText(this, toast, Toast.LENGTH_SHORT).show() - }.launchIn(appCoroutineScope) + }.launchIn(appCoroutineScope.get()) - autoGrantPermissionController.start() - keyEventRelayServiceWrapper.bind() + autoGrantPermissionController.get().start() + keyEventRelayServiceWrapper.get().bind() - if (systemBridgeConnectionManager.isConnected()) { + if (systemBridgeConnectionManager.get().isConnected()) { Timber.i("KeyMapperApp: System bridge is connected") } else { Timber.i("KeyMapperApp: System bridge is disconnected") } - systemBridgeAutoStarter.init() + systemBridgeAutoStarter.get().init() // Initialize SystemBridgeLogger to start receiving log messages from SystemBridge. // Using Lazy<> to avoid circular dependency issues and ensure it's only created // when the API level requirement is met. - systemBridgeLogger.start() + systemBridgeLogger.get().start() - appCoroutineScope.launch { - systemBridgeConnectionManager.connectionState.collect { state -> + appCoroutineScope.get().launch { + systemBridgeConnectionManager.get().connectionState.collect { state -> if (state is SystemBridgeConnectionState.Connected) { val isUsed = - settingsRepository.get(Keys.isSystemBridgeUsed).first() ?: false + settingsRepository.get().get(Keys.isSystemBridgeUsed).first() ?: false // Enable the setting to use PRO mode for key event actions the first time they use PRO mode. if (!isUsed) { - settingsRepository.set(Keys.keyEventActionsUseSystemBridge, true) + settingsRepository.get().set(Keys.keyEventActionsUseSystemBridge, true) } - settingsRepository.set(Keys.isSystemBridgeUsed, true) + settingsRepository.get().set(Keys.isSystemBridgeUsed, true) } } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/BootBroadcastReceiver.kt b/base/src/main/java/io/github/sds100/keymapper/base/BootBroadcastReceiver.kt index 7bb321b372..3d45969541 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/BootBroadcastReceiver.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/BootBroadcastReceiver.kt @@ -15,10 +15,13 @@ class BootBroadcastReceiver : BroadcastReceiver() { Timber.i( "Boot completed broadcast: time since boot = ${SystemClock.elapsedRealtime() / 1000}", ) + (context.applicationContext as? BaseKeyMapperApp)?.onBootUnlocked() } Intent.ACTION_LOCKED_BOOT_COMPLETED -> { - (context.applicationContext as? BaseKeyMapperApp)?.onBootUnlocked() + Timber.i( + "Locked boot completed broadcast: time since boot = ${SystemClock.elapsedRealtime() / 1000}", + ) } } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityService.kt b/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityService.kt index d247d41075..ba78fa0632 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityService.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityService.kt @@ -28,6 +28,7 @@ import androidx.savedstate.SavedStateRegistry import androidx.savedstate.SavedStateRegistryController import androidx.savedstate.SavedStateRegistryOwner import dagger.hilt.android.AndroidEntryPoint +import dagger.Lazy import io.github.sds100.keymapper.base.R import io.github.sds100.keymapper.base.actions.talkback.TalkBackGestureType import io.github.sds100.keymapper.base.actions.talkback.TalkbackGesturePerformer @@ -54,10 +55,16 @@ abstract class BaseAccessibilityService : SavedStateRegistryOwner { @Inject - lateinit var accessibilityServiceAdapter: AccessibilityServiceAdapterImpl + lateinit var accessibilityServiceAdapterLazy: Lazy + + val accessibilityServiceAdapter: AccessibilityServiceAdapterImpl + get() = accessibilityServiceAdapterLazy.get() @Inject - lateinit var inputMethodAdapter: InputMethodAdapter + lateinit var inputMethodAdapterLazy: Lazy + + val inputMethodAdapter: InputMethodAdapter + get() = inputMethodAdapterLazy.get() private var lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this) private var savedStateRegistryController: SavedStateRegistryController? =