diff --git a/.editorconfig b/.editorconfig
index 45db7af9a4..a55befd7a9 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -4,6 +4,6 @@ ktlint_function_naming_ignore_when_annotated_with = Composable
ktlint_ignore_back_ticked_identifier = true
ktlint_code_style = intellij_idea # Use IntelliJ style because it has trailing commas
-[app/src/main/java/io/github/sds100/keymapper/util/ui/compose/icons/*.{kt,kts}]
+[base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/*.{kt,kts}]
ktlint_standard_property-naming = disabled
ktlint_standard_backing-property-naming = disabled
\ No newline at end of file
diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
index b0a1c1f356..a2027703b1 100644
--- a/.github/workflows/pull-request.yml
+++ b/.github/workflows/pull-request.yml
@@ -21,7 +21,7 @@ jobs:
uses: android-actions/setup-android@v2
- name: Unit tests
- run: bash ./gradlew testFreeDebugUnitTest
+ run: bash ./gradlew testDebugUnitTest
style:
name: Code style check
@@ -96,10 +96,10 @@ jobs:
run: bundle exec fastlane testing
- name: set apk name env
- run: echo "APK_NAME=$(basename app/build/outputs/apk/free/ci/*.apk .apk)" >> $GITHUB_ENV
+ run: echo "APK_NAME=$(basename app/build/outputs/apk/ci/*.apk .apk)" >> $GITHUB_ENV
- name: Upload APK
uses: actions/upload-artifact@v4
with:
name: ${{ env.APK_NAME }}
- path: app/build/outputs/apk/free/ci/${{ env.APK_NAME }}.apk
+ path: app/build/outputs/apk/ci/${{ env.APK_NAME }}.apk
diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml
index 7bef735539..fd1b749380 100644
--- a/.github/workflows/testing.yml
+++ b/.github/workflows/testing.yml
@@ -27,7 +27,7 @@ jobs:
uses: android-actions/setup-android@v2
- name: Unit tests
- run: bash ./gradlew testFreeDebugUnitTest
+ run: bash ./gradlew testDebugUnitTest
style:
name: Code style check
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000000..74b4435ffe
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+KeyMapperFoss
\ No newline at end of file
diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml
new file mode 100644
index 0000000000..fd844d9451
--- /dev/null
+++ b/.idea/material_theme_project_new.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/navEditor.xml b/.idea/navEditor.xml
new file mode 100644
index 0000000000..7dbf723192
--- /dev/null
+++ b/.idea/navEditor.xml
@@ -0,0 +1,257 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index be4801e662..2bebdb6e56 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-## [3.1.2](https://github.com/sds100/KeyMapper/releases/tag/v3.1.2)
+## [3.2.0](https://github.com/sds100/KeyMapper/releases/tag/v3.2.0)
#### TO BE RELEASED
@@ -15,6 +15,7 @@
- #1686 (more fixes) do not show some screens behind system bars on the left/right side of the device.
- #1701 optimize the trigger screen for smaller screens so elements are less cut off.
- #1699 Do not highlight a floating button as if it is pressed after triggering a key event action from it.
+- Button to copy the key map UID to the clipboard is invisible on small screens.
## [3.1.1](https://github.com/sds100/KeyMapper/releases/tag/v3.1.1)
diff --git a/api/.gitignore b/api/.gitignore
new file mode 100644
index 0000000000..42afabfd2a
--- /dev/null
+++ b/api/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/api/build.gradle.kts b/api/build.gradle.kts
new file mode 100644
index 0000000000..046ff33114
--- /dev/null
+++ b/api/build.gradle.kts
@@ -0,0 +1,50 @@
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.google.devtools.ksp)
+ alias(libs.plugins.jlleitschuh.gradle.ktlint)
+ alias(libs.plugins.dagger.hilt.android)
+}
+
+android {
+ namespace = "io.github.sds100.keymapper.api"
+ compileSdk = libs.versions.compile.sdk.get().toInt()
+
+ defaultConfig {
+ minSdk = libs.versions.min.sdk.get().toInt()
+
+ consumerProguardFiles("consumer-rules.pro")
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = true
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro",
+ )
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+
+ buildFeatures {
+ aidl = true
+ }
+}
+
+dependencies {
+ implementation(project(":common"))
+ implementation(project(":base"))
+ implementation(project(":system"))
+
+ implementation(libs.jakewharton.timber)
+
+ implementation(libs.dagger.hilt.android)
+ ksp(libs.dagger.hilt.android.compiler)
+}
diff --git a/app/src/test/resources/backup-manager-test/empty.json b/api/consumer-rules.pro
similarity index 100%
rename from app/src/test/resources/backup-manager-test/empty.json
rename to api/consumer-rules.pro
diff --git a/api/proguard-rules.pro b/api/proguard-rules.pro
new file mode 100644
index 0000000000..481bb43481
--- /dev/null
+++ b/api/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/api/src/main/AndroidManifest.xml b/api/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..b3e73b7e65
--- /dev/null
+++ b/api/src/main/AndroidManifest.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/io/github/sds100/keymapper/api/Api.kt b/api/src/main/java/io/github/sds100/keymapper/api/Api.kt
similarity index 92%
rename from app/src/main/java/io/github/sds100/keymapper/api/Api.kt
rename to api/src/main/java/io/github/sds100/keymapper/api/Api.kt
index e14b0c974b..3c51a8126f 100644
--- a/app/src/main/java/io/github/sds100/keymapper/api/Api.kt
+++ b/api/src/main/java/io/github/sds100/keymapper/api/Api.kt
@@ -1,8 +1,5 @@
package io.github.sds100.keymapper.api
-/**
- * Created by sds100 on 17/06/2021.
- */
object Api {
// Do not use the package name for debug/ci builds
const val ACTION_TRIGGER_KEYMAP_BY_UID =
diff --git a/api/src/main/java/io/github/sds100/keymapper/api/ApiHiltModule.kt b/api/src/main/java/io/github/sds100/keymapper/api/ApiHiltModule.kt
new file mode 100644
index 0000000000..aaf5e6ce4b
--- /dev/null
+++ b/api/src/main/java/io/github/sds100/keymapper/api/ApiHiltModule.kt
@@ -0,0 +1,16 @@
+package io.github.sds100.keymapper.api
+
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import io.github.sds100.keymapper.system.apps.KeyMapShortcutActivityIntentBuilder
+
+@Module
+@InstallIn(SingletonComponent::class)
+abstract class ApiHiltModule {
+ @Binds
+ abstract fun bindKeyMapShortcutActivityIntentBuilder(
+ impl: KeyMapShortcutActivityIntentBuilderImpl,
+ ): KeyMapShortcutActivityIntentBuilder
+}
diff --git a/app/src/main/java/io/github/sds100/keymapper/api/KeyEventRelayService.kt b/api/src/main/java/io/github/sds100/keymapper/api/KeyEventRelayService.kt
similarity index 92%
rename from app/src/main/java/io/github/sds100/keymapper/api/KeyEventRelayService.kt
rename to api/src/main/java/io/github/sds100/keymapper/api/KeyEventRelayService.kt
index 333f14932d..58c49939e5 100644
--- a/app/src/main/java/io/github/sds100/keymapper/api/KeyEventRelayService.kt
+++ b/api/src/main/java/io/github/sds100/keymapper/api/KeyEventRelayService.kt
@@ -8,7 +8,6 @@ import android.os.IBinder
import android.os.IBinder.DeathRecipient
import android.view.KeyEvent
import android.view.MotionEvent
-import io.github.sds100.keymapper.system.inputmethod.KeyMapperImeHelper
import timber.log.Timber
import java.util.concurrent.ConcurrentHashMap
@@ -31,16 +30,29 @@ class KeyEventRelayService : Service() {
const val ACTION_REBIND_RELAY_SERVICE =
"io.github.sds100.keymapper.ACTION_REBIND_RELAY_SERVICE"
- const val CALLBACK_ID_ACCESSIBILITY_SERVICE = "accessibility_service"
- const val CALLBACK_ID_INPUT_METHOD = "input_method"
-
/**
* Used when a client registers a callback without specifying an ID.
*/
private const val CALLBACK_ID_DEFAULT = "default"
+
+ const val KEY_MAPPER_GUI_IME_PACKAGE =
+ "io.github.sds100.keymapper.inputmethod.latin"
+
+ private const val KEY_MAPPER_LEANBACK_IME_PACKAGE =
+ "io.github.sds100.keymapper.inputmethod.leanback"
+
+ private const val KEY_MAPPER_HACKERS_KEYBOARD_PACKAGE =
+ "io.github.sds100.keymapper.inputmethod.hackers"
}
- val permittedPackages = KeyMapperImeHelper.KEY_MAPPER_IME_PACKAGE_LIST
+ val permittedPackages by lazy {
+ arrayOf(
+ packageName,
+ KEY_MAPPER_GUI_IME_PACKAGE,
+ KEY_MAPPER_LEANBACK_IME_PACKAGE,
+ KEY_MAPPER_HACKERS_KEYBOARD_PACKAGE,
+ )
+ }
private val binderInterface: IKeyEventRelayService = object : IKeyEventRelayService.Stub() {
override fun sendKeyEvent(
diff --git a/api/src/main/java/io/github/sds100/keymapper/api/KeyMapShortcutActivityIntentBuilderImpl.kt b/api/src/main/java/io/github/sds100/keymapper/api/KeyMapShortcutActivityIntentBuilderImpl.kt
new file mode 100644
index 0000000000..f6d8faae4f
--- /dev/null
+++ b/api/src/main/java/io/github/sds100/keymapper/api/KeyMapShortcutActivityIntentBuilderImpl.kt
@@ -0,0 +1,22 @@
+package io.github.sds100.keymapper.api
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import dagger.hilt.android.qualifiers.ApplicationContext
+import io.github.sds100.keymapper.system.apps.KeyMapShortcutActivityIntentBuilder
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class KeyMapShortcutActivityIntentBuilderImpl @Inject constructor(
+ @ApplicationContext private val ctx: Context,
+) : KeyMapShortcutActivityIntentBuilder {
+ override fun build(intentAction: String, intentExtras: Bundle): Intent {
+ return Intent(ctx, LaunchKeyMapShortcutActivity::class.java).apply {
+ action = intentAction
+
+ putExtras(intentExtras)
+ }
+ }
+}
diff --git a/api/src/main/java/io/github/sds100/keymapper/api/LaunchKeyMapShortcutActivity.kt b/api/src/main/java/io/github/sds100/keymapper/api/LaunchKeyMapShortcutActivity.kt
new file mode 100644
index 0000000000..32e269e42b
--- /dev/null
+++ b/api/src/main/java/io/github/sds100/keymapper/api/LaunchKeyMapShortcutActivity.kt
@@ -0,0 +1,58 @@
+package io.github.sds100.keymapper.api
+
+import android.content.Intent
+import android.os.Bundle
+import android.widget.Toast
+import androidx.activity.ComponentActivity
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.system.accessibility.AccessibilityServiceAdapter
+import io.github.sds100.keymapper.system.accessibility.AccessibilityServiceState
+import javax.inject.Inject
+
+// DON'T MOVE THIS CLASS TO A DIFFERENT PACKAGE BECAUSE IT BREAKS THE API
+/**
+ * Use basic Activity, NOT AppCompatActivity so the NoDisplay theme works. Otherwise an
+ * exception may be thrown because the theme doesn't extend AppCompat.
+ */
+
+@AndroidEntryPoint
+class LaunchKeyMapShortcutActivity : ComponentActivity() {
+
+ @Inject
+ lateinit var accessibilityServiceAdapter: AccessibilityServiceAdapter
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val accessibilityServiceState = accessibilityServiceAdapter.state.value
+
+ when (accessibilityServiceState) {
+ AccessibilityServiceState.ENABLED ->
+ if (intent.action == Api.ACTION_TRIGGER_KEYMAP_BY_UID) {
+ Intent(Api.ACTION_TRIGGER_KEYMAP_BY_UID).apply {
+ setPackage(packageName)
+
+ val uuid = intent.getStringExtra(Api.EXTRA_KEYMAP_UID)
+ putExtra(Api.EXTRA_KEYMAP_UID, uuid)
+
+ sendBroadcast(this)
+ }
+ }
+
+ AccessibilityServiceState.CRASHED -> Toast.makeText(
+ this,
+ R.string.error_accessibility_service_crashed,
+ Toast.LENGTH_SHORT,
+ ).show()
+
+ AccessibilityServiceState.DISABLED -> Toast.makeText(
+ this,
+ R.string.error_accessibility_service_disabled,
+ Toast.LENGTH_SHORT,
+ ).show()
+ }
+
+ finish()
+ }
+}
diff --git a/app/src/main/java/io/github/sds100/keymapper/api/PauseMappingsBroadcastReceiver.kt b/api/src/main/java/io/github/sds100/keymapper/api/PauseMappingsBroadcastReceiver.kt
similarity index 73%
rename from app/src/main/java/io/github/sds100/keymapper/api/PauseMappingsBroadcastReceiver.kt
rename to api/src/main/java/io/github/sds100/keymapper/api/PauseMappingsBroadcastReceiver.kt
index 662687d589..8cabcafc4c 100644
--- a/app/src/main/java/io/github/sds100/keymapper/api/PauseMappingsBroadcastReceiver.kt
+++ b/api/src/main/java/io/github/sds100/keymapper/api/PauseMappingsBroadcastReceiver.kt
@@ -3,21 +3,21 @@ package io.github.sds100.keymapper.api
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
-import io.github.sds100.keymapper.UseCases
-import io.github.sds100.keymapper.util.firstBlocking
-
-/**
- * Created by sds100 on 17/06/2021.
- */
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.sds100.keymapper.base.keymaps.PauseKeyMapsUseCase
+import io.github.sds100.keymapper.common.utils.firstBlocking
+import javax.inject.Inject
// DON'T MOVE THIS CLASS TO A DIFFERENT PACKAGE BECAUSE IT BREAKS THE API
+@AndroidEntryPoint
class PauseMappingsBroadcastReceiver : BroadcastReceiver() {
+ @Inject
+ lateinit var useCase: PauseKeyMapsUseCase
+
override fun onReceive(context: Context?, intent: Intent?) {
context ?: return
- val useCase = UseCases.pauseKeyMaps(context)
-
when (intent?.action) {
Api.ACTION_PAUSE_MAPPINGS -> useCase.pause()
Api.ACTION_RESUME_MAPPINGS -> useCase.resume()
diff --git a/app/src/main/java/io/github/sds100/keymapper/api/TriggerKeyMapsBroadcastReceiver.kt b/api/src/main/java/io/github/sds100/keymapper/api/TriggerKeyMapsBroadcastReceiver.kt
similarity index 55%
rename from app/src/main/java/io/github/sds100/keymapper/api/TriggerKeyMapsBroadcastReceiver.kt
rename to api/src/main/java/io/github/sds100/keymapper/api/TriggerKeyMapsBroadcastReceiver.kt
index ffb5e42468..12b0acdf8e 100644
--- a/app/src/main/java/io/github/sds100/keymapper/api/TriggerKeyMapsBroadcastReceiver.kt
+++ b/api/src/main/java/io/github/sds100/keymapper/api/TriggerKeyMapsBroadcastReceiver.kt
@@ -3,25 +3,32 @@ package io.github.sds100.keymapper.api
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
-import io.github.sds100.keymapper.ServiceLocator
-import io.github.sds100.keymapper.util.ServiceEvent
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.sds100.keymapper.base.keymaps.TriggerKeyMapEvent
+import io.github.sds100.keymapper.system.accessibility.AccessibilityServiceAdapter
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
+import javax.inject.Inject
// DON'T MOVE THIS CLASS TO A DIFFERENT PACKAGE BECAUSE IT BREAKS THE API
+@AndroidEntryPoint
class TriggerKeyMapsBroadcastReceiver : BroadcastReceiver() {
+ @Inject
+ lateinit var serviceAdapter: AccessibilityServiceAdapter
+
+ @Inject
+ lateinit var coroutineScope: CoroutineScope
+
override fun onReceive(context: Context?, intent: Intent?) {
context ?: return
intent ?: return
- val serviceAdapter = ServiceLocator.accessibilityServiceAdapter(context)
- val scope = ServiceLocator.appCoroutineScope(context)
-
when (intent.action) {
Api.ACTION_TRIGGER_KEYMAP_BY_UID -> {
intent.getStringExtra(Api.EXTRA_KEYMAP_UID)?.let { uid ->
- scope.launch {
- serviceAdapter.send(ServiceEvent.TriggerKeyMap(uid))
+ coroutineScope.launch {
+ serviceAdapter.send(TriggerKeyMapEvent(uid))
}
}
}
diff --git a/app/build.gradle b/app/build.gradle
deleted file mode 100644
index aad6da2e5a..0000000000
--- a/app/build.gradle
+++ /dev/null
@@ -1,282 +0,0 @@
-apply plugin: "com.android.application"
-apply plugin: "kotlin-android"
-apply plugin: "kotlin-kapt"
-apply plugin: "com.google.devtools.ksp"
-apply plugin: "androidx.navigation.safeargs.kotlin"
-apply plugin: "kotlinx-serialization"
-apply plugin: "org.jetbrains.kotlin.plugin.parcelize"
-apply plugin: "org.jlleitschuh.gradle.ktlint"
-apply plugin: "org.jetbrains.kotlin.plugin.compose"
-apply plugin: "androidx.room"
-
-android {
-
- namespace "io.github.sds100.keymapper"
- compileSdk 35
- buildToolsVersion = "35.0.0"
-
- def versionProperties = new Properties()
- file("version.properties").withInputStream { versionProperties.load(it) }
-
- defaultConfig {
- applicationId "io.github.sds100.keymapper"
- minSdkVersion 21
- targetSdkVersion 35
- versionCode versionProperties.getProperty("VERSION_CODE").toInteger()
- versionName versionProperties.getProperty("VERSION_NAME")
- multiDexEnabled true
-
- vectorDrawables.useSupportLibrary = true
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
-
- javaCompileOptions {
- annotationProcessorOptions {
- arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
- }
- }
- }
-
- signingConfigs {
- release {
- storeFile file("keystore.jks")
- storePassword System.getenv("KEYSTORE_PASSWORD")
- keyAlias "keymapper"
- keyPassword System.getenv("KEY_PASSWORD")
- }
- }
-
- buildTypes {
-
- release {
- minifyEnabled true
- proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
- signingConfig signingConfigs.release
- }
-
- debug {
- applicationIdSuffix ".debug"
- versionNameSuffix "-debug"
- }
-
- debug_release {
- // Extend from debug build type so compose Live Edit and rapid building works
- initWith debug
-
- // Do not alter the package name so can test revenuecat and billing while developing.
- applicationIdSuffix ""
-
- /*
- This is required because the splitties library does not have a debug_release build type.
- */
- matchingFallbacks = ["debug"]
- }
-
- ci {
- minifyEnabled true
- shrinkResources true
-
- /*
- This is required because the splitties library does not have a ci build type.
- */
- matchingFallbacks = ["debug"]
-
- applicationIdSuffix ".ci"
- versionNameSuffix "-ci." + versionProperties.getProperty("VERSION_NUM")
-
- proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
-
- signingConfig signingConfigs.debug
- }
- }
-
- flavorDimensions = ["pro"]
- productFlavors {
- free {
- dimension "pro"
- }
- pro {
- dimension "pro"
-
- File file = rootProject.file("local.properties")
- String keyName = "REVENUECAT_API_KEY"
-
- if (file.exists()) {
- def localProperties = new Properties()
- localProperties.load(new FileInputStream(file))
- if (localProperties.containsKey(keyName)) {
- buildConfigField("String", keyName, localProperties[keyName])
- }
- }
- }
- }
-
- buildFeatures {
- dataBinding true
- viewBinding true
- aidl true
- buildConfig true
- compose true
- }
-
- compileOptions {
- // Required for desugaring new Java time API on lower than API 26
- coreLibraryDesugaringEnabled = true
-
- sourceCompatibility JavaVersion.VERSION_17
- targetCompatibility JavaVersion.VERSION_17
- }
-
- kotlinOptions {
- jvmTarget = "17"
- }
-
- kapt {
- correctErrorTypes = true
- }
-
- composeOptions {
- kotlinCompilerExtensionVersion "1.5.10"
- }
-
- sourceSets {
- androidTest {
- assets.srcDirs += files("$projectDir/schemas".toString())
- resources.srcDirs += ["src/test/resources"]
- }
-
- test {
- java.srcDirs += ["src/pro/test/java"]
- }
- }
-
- applicationVariants.configureEach { variant ->
- variant.outputs.configureEach {
- outputFileName = "keymapper-${variant.versionName}.apk"
- }
- }
-
- room {
- schemaDirectory "$projectDir/schemas"
- }
-}
-
-dependencies {
- implementation fileTree(include: ["*.jar"], dir: "libs")
-
- compileOnly project(":systemstubs")
-
- def room_version = "2.7.1"
- def coroutinesVersion = "1.9.0"
- def nav_version = '2.9.0'
- def epoxy_version = "4.6.2"
- def splitties_version = "3.0.0"
- def multidex_version = "2.0.1"
- def shizuku_version = "13.1.5"
-
- // kotlin stuff
- implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
- implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0"
-
- // random stuff
- implementation "com.google.android.material:material:1.13.0-alpha13"
- implementation "com.github.salomonbrys.kotson:kotson:2.5.0"
- implementation "com.airbnb.android:epoxy:$epoxy_version"
- implementation "com.airbnb.android:epoxy-databinding:$epoxy_version"
- kapt "com.airbnb.android:epoxy-processor:$epoxy_version"
- implementation "com.jakewharton.timber:timber:5.0.1"
- implementation "net.lingala.zip4j:zip4j:2.8.0"
- implementation "com.anggrayudi:storage:0.8.1"
- implementation "com.github.MFlisar:DragSelectRecyclerView:0.3"
- implementation "com.google.android.flexbox:flexbox:3.0.0"
- implementation "dev.rikka.shizuku:api:$shizuku_version"
- implementation "dev.rikka.shizuku:provider:$shizuku_version"
- implementation "org.lsposed.hiddenapibypass:hiddenapibypass:4.3"
- proImplementation 'com.revenuecat.purchases:purchases:8.17.1'
- proImplementation "com.airbnb.android:lottie-compose:6.6.3"
- implementation("com.squareup.okhttp3:okhttp:4.12.0")
- coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
- implementation 'com.canopas.intro-showcase-view:introshowcaseview:2.0.2'
-
- // splitties
- implementation "com.louiscad.splitties:splitties-bitflags:$splitties_version"
- implementation "com.louiscad.splitties:splitties-alertdialog-appcompat-coroutines:$splitties_version"
- implementation("com.louiscad.splitties:splitties-alertdialog-material:$splitties_version")
- implementation "com.louiscad.splitties:splitties-snackbar:$splitties_version"
- implementation "com.louiscad.splitties:splitties-toast:$splitties_version"
- implementation "com.louiscad.splitties:splitties-mainthread:$splitties_version"
-
- // androidx
- implementation "androidx.legacy:legacy-support-core-ui:1.0.0"
- implementation "androidx.core:core-ktx:1.16.0"
-
- implementation "androidx.activity:activity-ktx:1.10.1"
- implementation "androidx.fragment:fragment-ktx:1.8.6"
- implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.0"
- implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.9.0"
- implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.9.0"
- implementation "androidx.room:room-ktx:$room_version"
- implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
- implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
- implementation "androidx.multidex:multidex:$multidex_version"
- implementation "androidx.appcompat:appcompat:1.7.0"
- implementation "androidx.recyclerview:recyclerview:1.4.0"
- implementation "androidx.preference:preference-ktx:1.2.1"
- implementation "androidx.constraintlayout:constraintlayout:2.2.1"
- implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
- implementation "androidx.room:room-runtime:$room_version"
- implementation "androidx.viewpager2:viewpager2:1.1.0"
- implementation "androidx.datastore:datastore-preferences:1.2.0-alpha02"
- implementation "androidx.core:core-splashscreen:1.0.1"
- implementation "androidx.activity:activity-compose:1.10.1"
- implementation "androidx.navigation:navigation-compose:2.9.0"
- implementation "androidx.navigation:navigation-fragment-compose:2.9.0"
- ksp "androidx.room:room-compiler:$room_version"
-
- // Compose
- // Downgrade compose BOM version because there is a crash. Do not use 2025.05.00
- Dependency composeBom = platform('androidx.compose:compose-bom:2025.04.01')
- implementation composeBom
- implementation 'androidx.compose.foundation:foundation'
- implementation "androidx.compose.ui:ui-android"
- implementation "androidx.compose.material3:material3-android"
- implementation "androidx.compose.ui:ui-tooling-preview-android"
- implementation "androidx.compose.material:material-icons-extended-android"
- implementation 'androidx.compose.material3.adaptive:adaptive-android'
- implementation "androidx.compose.material3.adaptive:adaptive-navigation"
- implementation "com.google.accompanist:accompanist-drawablepainter:0.35.0-alpha"
- implementation "androidx.activity:activity-compose:1.10.1"
- debugImplementation "androidx.compose.ui:ui-tooling"
- debug_releaseImplementation "androidx.compose.ui:ui-tooling"
-
-// debugImplementation "com.squareup.leakcanary:leakcanary-android:2.6"
-
- def junitVersion = "4.13.2"
- def androidXTestExtKotlinRunnerVersion = "1.2.1"
- def espressoVersion = "3.6.1"
- def androidXTestCoreVersion = "1.6.1"
-
- // Dependencies for local unit tests
- testImplementation "junit:junit:$junitVersion"
- testImplementation "org.hamcrest:hamcrest-all:1.3"
- testImplementation "androidx.test.ext:junit-ktx:$androidXTestExtKotlinRunnerVersion"
- testImplementation "androidx.test:core-ktx:1.6.1"
- testImplementation "org.robolectric:robolectric:4.14.1"
- testImplementation "androidx.arch.core:core-testing:2.2.0"
- testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion"
- testImplementation "pl.pragmatists:JUnitParams:1.1.1"
- testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0"
- testImplementation "org.mockito:mockito-core:5.15.2"
- testImplementation "org.mockito:mockito-inline:5.2.0"
-
- androidTestImplementation "androidx.test.ext:junit:$androidXTestExtKotlinRunnerVersion"
- androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
- androidTestImplementation "androidx.arch.core:core-testing:2.2.0"
- androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion"
- androidTestImplementation "junit:junit:$junitVersion"
- androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"
- androidTestImplementation "android.arch.persistence.room:testing:1.1.1"
- androidTestImplementation "org.mockito:mockito-android:4.6.1"
- debugImplementation "androidx.fragment:fragment-testing:1.8.6"
- implementation "androidx.test:core:$androidXTestCoreVersion"
-}
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
new file mode 100644
index 0000000000..9f3e80bb1f
--- /dev/null
+++ b/app/build.gradle.kts
@@ -0,0 +1,180 @@
+@file:Suppress("UnstableApiUsage")
+
+import java.util.Properties
+
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.kotlin.compose)
+ alias(libs.plugins.kotlin.kapt)
+ alias(libs.plugins.kotlin.serialization)
+ alias(libs.plugins.kotlin.parcelize)
+ alias(libs.plugins.androidx.navigation.safeargs.kotlin)
+ alias(libs.plugins.google.devtools.ksp)
+ alias(libs.plugins.jlleitschuh.gradle.ktlint)
+ alias(libs.plugins.dagger.hilt.android)
+}
+
+android {
+ namespace = "io.github.sds100.keymapper"
+ compileSdk = libs.versions.compile.sdk.get().toInt()
+ buildToolsVersion = libs.versions.build.tools.get()
+
+ val versionProperties = Properties().apply {
+ project.file("version.properties").inputStream().use { load(it) }
+ }
+
+ defaultConfig {
+ applicationId = "io.github.sds100.keymapper"
+ minSdk = libs.versions.min.sdk.get().toInt()
+ targetSdk = libs.versions.target.sdk.get().toInt()
+
+ versionCode = versionProperties.getProperty("VERSION_CODE").toInt()
+ versionName = versionProperties.getProperty("VERSION_NAME")
+ multiDexEnabled = true
+
+ vectorDrawables.useSupportLibrary = true
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ signingConfigs {
+ create("release") {
+ storeFile = file("keystore.jks")
+ storePassword = System.getenv("KEYSTORE_PASSWORD")
+ keyAlias = "keymapper"
+ keyPassword = System.getenv("KEY_PASSWORD")
+ }
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = true
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro",
+ )
+ signingConfig = signingConfigs.getByName("release")
+ versionNameSuffix = "-foss"
+ }
+
+ debug {
+ applicationIdSuffix = ".debug"
+ versionNameSuffix = "-foss-debug"
+ }
+
+ create("debug_release") {
+ initWith(getByName("debug"))
+ applicationIdSuffix = "" // Reset from debug
+ matchingFallbacks.add("debug")
+ }
+
+ create("ci") {
+ isMinifyEnabled = true
+ // shrinkResources is now part of isMinifyEnabled in newer AGP,
+ // but let's keep explicit if an older AGP interpretation is in mind.
+ // If build fails, this might need adjustment.
+ // For AGP 8.x, shrinkResources is controlled by isMinifyEnabled.
+ // Explicitly setting it might be deprecated or have no effect.
+ // I'll assume it implies full R8 shrinkage.
+ // shrinkResources = true // This property might not exist directly here in KTS for AGP 8+
+ // Instead, you rely on isMinifyEnabled and Proguard rules.
+
+ matchingFallbacks.add("debug")
+ applicationIdSuffix = ".ci"
+ versionNameSuffix = "-foss-ci.${versionProperties.getProperty("VERSION_NUM")}"
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro",
+ )
+ signingConfig = signingConfigs.getByName("debug") // Assuming debug signing for CI
+ }
+ }
+
+ buildFeatures {
+ dataBinding = true
+ aidl = true
+ buildConfig = true
+ compose = true
+ }
+
+ compileOptions {
+ isCoreLibraryDesugaringEnabled = true
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+
+ kapt {
+ correctErrorTypes = true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
+ }
+
+ sourceSets {
+ getByName("androidTest") {
+ assets.srcDirs(files("$projectDir/schemas"))
+ resources.srcDirs("src/test/resources")
+ }
+ getByName("test") {
+ java.srcDirs("src/pro/test/java")
+ }
+ }
+
+ applicationVariants.all {
+ outputs.all {
+ val output = this as com.android.build.gradle.internal.api.BaseVariantOutputImpl
+ output.outputFileName = "keymapper-foss-${'$'}{variant.versionName}.apk"
+ }
+ }
+}
+
+dependencies {
+ implementation(fileTree(mapOf("include" to listOf("*.jar"), "dir" to "libs")))
+
+ implementation(project(":common"))
+ implementation(project(":base"))
+ implementation(project(":api"))
+ implementation(project(":data"))
+ implementation(project(":system"))
+ compileOnly(project(":systemstubs"))
+
+ coreLibraryDesugaring(libs.desugar.jdk.libs)
+
+ // Other
+ implementation(libs.jakewharton.timber)
+
+ // Androidx
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.lifecycle.runtime.ktx)
+ implementation(libs.androidx.activity.compose)
+ implementation(platform(libs.androidx.compose.bom))
+ implementation(libs.androidx.multidex)
+ implementation(libs.androidx.appcompat)
+ implementation(libs.bundles.androidx.navigation)
+
+ // Compose
+ implementation(platform(libs.androidx.compose.bom))
+ implementation(libs.androidx.compose.foundation)
+ implementation(libs.androidx.compose.ui.android)
+ implementation(libs.androidx.compose.material3)
+ implementation(libs.androidx.compose.ui.tooling.preview)
+ implementation(libs.androidx.compose.material.icons.extended)
+ implementation(libs.androidx.compose.material3.adaptive)
+ implementation(libs.androidx.compose.material3.adaptive.navigation)
+ implementation(libs.androidx.hilt.navigation.compose)
+ implementation(libs.google.accompanist.drawablepainter)
+ implementation(libs.androidx.compose.ui.tooling)
+
+ // Dagger
+ implementation(libs.dagger.hilt.android)
+ ksp(libs.dagger.hilt.android.compiler)
+
+ debugImplementation(libs.androidx.ui.tooling)
+
+ testImplementation(libs.junit)
+}
diff --git a/app/src/androidTest/java/io/github/sds100/keymapper/AppDatabaseMigrationTest.kt b/app/src/androidTest/java/io/github/sds100/keymapper/AppDatabaseMigrationTest.kt
deleted file mode 100644
index 83dc9f3ce9..0000000000
--- a/app/src/androidTest/java/io/github/sds100/keymapper/AppDatabaseMigrationTest.kt
+++ /dev/null
@@ -1,858 +0,0 @@
-package io.github.sds100.keymapper
-
-import androidx.datastore.preferences.core.PreferenceDataStoreFactory
-import androidx.datastore.preferences.core.edit
-import androidx.datastore.preferences.core.stringPreferencesKey
-import androidx.room.migration.Migration
-import androidx.room.testing.MigrationTestHelper
-import androidx.sqlite.db.SupportSQLiteDatabase
-import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
-import androidx.test.espresso.matcher.ViewMatchers
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import com.github.salomonbrys.kotson.get
-import com.google.gson.Gson
-import com.google.gson.JsonArray
-import com.google.gson.JsonElement
-import com.google.gson.JsonParseException
-import com.google.gson.JsonParser
-import io.github.sds100.keymapper.data.db.AppDatabase
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.MainScope
-import kotlinx.coroutines.runBlocking
-import org.hamcrest.Matchers
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import java.io.File
-import java.io.IOException
-
-/**
- * Created by sds100 on 05/06/20.
- */
-@ExperimentalCoroutinesApi
-@RunWith(AndroidJUnit4::class)
-class AppDatabaseMigrationTest {
- companion object {
- private const val TEST_DB_NAME = "migration_test"
-
- private val MIGRATION_1_2_TEST_DATA = arrayOf(
- arrayOf(1, "[]", 0, 1, "NULL", "NULL", "NULL"),
- arrayOf(2, "[{\"keys\":[25]}]", 4, 1, "APP", "com.android.chrome", "[]"),
- arrayOf(3, "[{\"keys\":[25,24]}]", 0, 1, "KEY", "24", "[]"),
- arrayOf(4, "[{\"keys\":[25,24]}]", 0, 1, "KEYCODE", "24", "[]"),
- arrayOf(
- 5,
- "[{\"keys\":[25,24]},{\"keys\":[25]}]",
- 0,
- 1,
- "SYSTEM_ACTION",
- "toggle_flashlight",
- "[{\"data\":\"option_lens_back\",\"id\":\"extra_flash\"}]",
- ),
- arrayOf(6, "[{\"keys\":[4]}]", 3, 1, "SYSTEM_ACTION", "volume_mute", "[]"),
- )
-
- private val MIGRATION_1_2_EXPECTED_DATA = arrayOf(
- arrayOf(1, "{\"extras\":[],\"keys\":[],\"mode\":1}", "[]", "[]", 1, 0, "NULL", 1),
- arrayOf(
- 2,
- "{\"extras\":[],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.ANY_DEVICE\",\"keyCode\":25}],\"mode\":1}",
- "[{\"data\":\"com.android.chrome\",\"extras\":[],\"flags\":0,\"type\":\"APP\"}]",
- "[]",
- 1,
- 1,
- "NULL",
- 1,
- ),
- arrayOf(
- 3,
- "{\"extras\":[],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.ANY_DEVICE\",\"keyCode\":25},{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.ANY_DEVICE\",\"keyCode\":24}],\"mode\":0}",
- "[{\"data\":\"24\",\"extras\":[],\"flags\":0,\"type\":\"KEY_EVENT\"}]",
- "[]",
- 1,
- 0,
- "NULL",
- 1,
- ),
- arrayOf(
- 4,
- "{\"extras\":[],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.ANY_DEVICE\",\"keyCode\":25},{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.ANY_DEVICE\",\"keyCode\":24}],\"mode\":0}",
- "[{\"data\":\"24\",\"extras\":[],\"flags\":0,\"type\":\"KEY_EVENT\"}]",
- "[]",
- 1,
- 0,
- "NULL",
- 1,
- ),
- arrayOf(
- 5,
- "{\"extras\":[],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.ANY_DEVICE\",\"keyCode\":25},{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.ANY_DEVICE\",\"keyCode\":24}],\"mode\":0}",
- "[{\"data\":\"toggle_flashlight\",\"extras\":[{\"data\":\"option_lens_back\",\"id\":\"extra_flash\"}],\"flags\":0,\"type\":\"SYSTEM_ACTION\"}]",
- "[]",
- 1,
- 0,
- "NULL",
- 1,
- ),
- arrayOf(
- 6,
- "{\"extras\":[],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.ANY_DEVICE\",\"keyCode\":25}],\"mode\":1}",
- "[{\"data\":\"toggle_flashlight\",\"extras\":[{\"data\":\"option_lens_back\",\"id\":\"extra_flash\"}],\"flags\":0,\"type\":\"SYSTEM_ACTION\"}]",
- "[]",
- 1,
- 0,
- "NULL",
- 1,
- ),
- arrayOf(
- 7,
- "{\"extras\":[],\"keys\":[{\"clickType\":1,\"deviceId\":\"io.github.sds100.keymapper.ANY_DEVICE\",\"keyCode\":4}],\"mode\":1}",
- "[{\"data\":\"volume_mute\",\"extras\":[],\"flags\":1,\"type\":\"SYSTEM_ACTION\"}]",
- "[]",
- 1,
- 0,
- "NULL",
- 1,
- ),
- )
-
- private val MIGRATION_2_3_TEST_DATA = arrayOf(
- arrayOf(
- 1,
- "{\"extras\":[{\"data\":\"610\",\"id\":\"extra_repeat_delay\"}],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25}],\"mode\":1}",
- "[{\"data\":\"10\",\"extras\":[],\"flags\":0,\"type\":\"KEY_EVENT\"}]",
- "[]",
- 1,
- 1,
- "NULL",
- 1,
- ),
- arrayOf(2, "{\"extras\":[],\"keys\":[],\"mode\":1}", "[]", "[]", 1, 0, "NULL", 1),
- arrayOf(
- 3,
- "{\"extras\":[],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25},{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":24}],\"mode\":0}",
- "[{\"data\":\"enable_mobile_data\",\"extras\":[],\"flags\":0,\"type\":\"SYSTEM_ACTION\"}]",
- "[]",
- 1,
- 0,
- "NULL",
- 1,
- ),
- arrayOf(
- 4,
- "{\"extras\":[],\"keys\":[{\"clickType\":1,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25},{\"clickType\":1,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":24}],\"mode\":0}",
- "[{\"data\":\"14\",\"extras\":[],\"flags\":0,\"type\":\"KEY_EVENT\"}]",
- "[]",
- 1,
- 0,
- "NULL",
- 1,
- ),
- )
-
- private val MIGRATION_2_3_EXPECTED_DATA = arrayOf(
- arrayOf(
- 1,
- "{\"extras\":[{\"data\":\"610\",\"id\":\"extra_repeat_delay\"}],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25}],\"mode\":1}",
- "[{\"data\":\"10\",\"extras\":[],\"flags\":0,\"type\":\"KEY_EVENT\"}]",
- "[]",
- 1,
- 17,
- "NULL",
- 1,
- ),
- arrayOf(2, "{\"extras\":[],\"keys\":[],\"mode\":1}", "[]", "[]", 1, 0, "NULL", 1),
- arrayOf(
- 3,
- "{\"extras\":[],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25},{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":24}],\"mode\":0}",
- "[{\"data\":\"enable_mobile_data\",\"extras\":[],\"flags\":0,\"type\":\"SYSTEM_ACTION\"}]",
- "[]",
- 1,
- 0,
- "NULL",
- 1,
- ),
- arrayOf(
- 4,
- "{\"extras\":[],\"keys\":[{\"clickType\":1,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25},{\"clickType\":1,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":24}],\"mode\":0}",
- "[{\"data\":\"14\",\"extras\":[],\"flags\":0,\"type\":\"KEY_EVENT\"}]",
- "[]",
- 1,
- 16,
- "NULL",
- 1,
- ),
- )
-
- private val MIGRATION_3_4_TEST_DATA = arrayOf(
- arrayOf(
- 1,
- "{\"extras\":[{\"data\":\"610\",\"id\":\"extra_repeat_delay\"}],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25}],\"mode\":1}",
- "[{\"data\":\"10\",\"extras\":[],\"flags\":0,\"type\":\"KEY_EVENT\"}]",
- "[]",
- 1,
- 17,
- "NULL",
- 1,
- ),
- arrayOf(2, "{\"extras\":[],\"keys\":[],\"mode\":1}", "[]", "[]", 1, 0, "NULL", 1),
- arrayOf(
- 3,
- "{\"extras\":[],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25},{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":24}],\"mode\":0}",
- "[{\"data\":\"enable_mobile_data\",\"extras\":[],\"flags\":0,\"type\":\"SYSTEM_ACTION\"}]",
- "[]",
- 1,
- 0,
- "NULL",
- 1,
- ),
- arrayOf(
- 4,
- "{\"extras\":[],\"keys\":[{\"clickType\":1,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25},{\"clickType\":1,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":24}],\"mode\":0}",
- "[{\"data\":\"14\",\"extras\":[],\"flags\":0,\"type\":\"KEY_EVENT\"}]",
- "[]",
- 1,
- 16,
- "NULL",
- 1,
- ),
- )
-
- private val MIGRATION_3_4_EXPECTED_DATA = arrayOf(
- arrayOf(
- 1,
- "{\"extras\":[{\"data\":\"610\",\"id\":\"extra_repeat_delay\"}],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25}],\"mode\":2}",
- "[{\"data\":\"10\",\"extras\":[],\"flags\":0,\"type\":\"KEY_EVENT\"}]",
- "[]",
- 1,
- 17,
- "NULL",
- 1,
- ),
- arrayOf(2, "{\"extras\":[],\"keys\":[],\"mode\":2}", "[]", "[]", 1, 0, "NULL", 1),
- arrayOf(
- 3,
- "{\"extras\":[],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25},{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":24}],\"mode\":0}",
- "[{\"data\":\"enable_mobile_data\",\"extras\":[],\"flags\":0,\"type\":\"SYSTEM_ACTION\"}]",
- "[]",
- 1,
- 0,
- "NULL",
- 1,
- ),
- arrayOf(
- 4,
- "{\"extras\":[],\"keys\":[{\"clickType\":1,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25},{\"clickType\":1,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":24}],\"mode\":0}",
- "[{\"data\":\"14\",\"extras\":[],\"flags\":0,\"type\":\"KEY_EVENT\"}]",
- "[]",
- 1,
- 16,
- "NULL",
- 1,
- ),
- )
-
- private val MIGRATION_4_5_TEST_DATA = arrayOf(
- arrayOf(
- 1,
- "{\"extras\":[],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25},{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":24}],\"mode\":0}",
- "[{\"data\":\"volume_up\",\"extras\":[],\"flags\":1,\"type\":\"SYSTEM_ACTION\"},{\"data\":\"com.android.settings\",\"extras\":[],\"flags\":0,\"type\":\"APP\"}]",
- "[]",
- 1,
- 16,
- "NULL",
- 1,
- ),
- arrayOf(
- 2,
- "{\"extras\":[{\"data\":\"5000\",\"id\":\"extra_hold_down_until_repeat_delay\"},{\"data\":\"575\",\"id\":\"extra_repeat_delay\"},{\"data\":\"365\",\"id\":\"extra_vibration_duration\"}],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25}],\"mode\":2}",
- "[{\"data\":\"7\",\"extras\":[],\"flags\":0,\"type\":\"KEY_EVENT\"}]",
- "[]",
- 1,
- 19,
- "NULL",
- 1,
- ),
- )
-
- private val MIGRATION_4_5_EXPECTED_DATA = arrayOf(
- arrayOf(
- 1,
- "{\"extras\":[],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25},{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":24}],\"mode\":0}",
- "[{\"data\":\"volume_up\",\"extras\":[],\"flags\":5,\"type\":\"SYSTEM_ACTION\"},{\"data\":\"com.android.settings\",\"extras\":[],\"flags\":4,\"type\":\"APP\"}]",
- "[]",
- 1,
- 0,
- "NULL",
- 1,
- ),
- arrayOf(
- 2,
- "{\"extras\":[{\"data\":\"365\",\"id\":\"extra_vibration_duration\"}],\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25}],\"mode\":2}",
- "[{\"data\":\"7\",\"extras\":[{\"data\":\"5000\",\"id\":\"extra_hold_down_until_repeat_delay\"},{\"data\":\"575\",\"id\":\"extra_repeat_delay\"}],\"flags\":6,\"type\":\"KEY_EVENT\"}]",
- "[]",
- 1,
- 1,
- "NULL",
- 1,
- ),
- )
-
- private val MIGRATION_5_6_TEST_DATA = arrayOf(
- arrayOf(
- 1,
- "{\"extras\":[{\"data\":\"2930\",\"id\":\"extra_sequence_trigger_timeout\"},{\"data\":\"1840\",\"id\":\"extra_long_press_delay\"},{\"data\":\"3580\",\"id\":\"extra_double_press_timeout\"},{\"data\":\"390\",\"id\":\"extra_vibration_duration\"}],\"keys\":[{\"clickType\":1,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25},{\"clickType\":2,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":24}],\"mode\":1}",
- "[{\"data\":\"volume_up\",\"extras\":[],\"flags\":1,\"type\":\"SYSTEM_ACTION\"}]",
- "[]",
- 1,
- 5,
- "NULL",
- 1,
- ),
- )
-
- private val MIGRATION_5_6_EXPECTED_DATA = arrayOf(
- arrayOf(
- 1,
- "{\"extras\":[{\"data\":\"2930\",\"id\":\"extra_sequence_trigger_timeout\"},{\"data\":\"1840\",\"id\":\"extra_long_press_delay\"},{\"data\":\"3580\",\"id\":\"extra_double_press_timeout\"},{\"data\":\"390\",\"id\":\"extra_vibration_duration\"}],\"flags\":5,\"keys\":[{\"clickType\":1,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":25},{\"clickType\":2,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"keyCode\":24}],\"mode\":1}",
- "[{\"data\":\"volume_up\",\"extras\":[],\"flags\":1,\"type\":\"SYSTEM_ACTION\"}]",
- "[]",
- 1,
- 0,
- "NULL",
- 1,
- ),
- )
-
- private val MIGRATION_9_10_TEST_DATA = arrayOf(
- arrayOf(
- 1,
- "{\"extras\":[],\"flags\":0,\"keys\":[],\"mode\":2}",
- "[{\"data\":\"com.google.android.contacts\",\"extras\":[],\"flags\":2,\"type\":\"APP\",\"uid\":\"dc2d8c69-aaa5-4471-b981-17cba7677c1a\"}]",
- "[]",
- 1,
- 0,
- "",
- 1,
- "d314e9e8-fac9-43e7-b540-0b9c0bfb4238",
- ),
- arrayOf(
- 2,
- "{\"extras\":[],\"flags\":1,\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"flags\":0,\"keyCode\":25,\"uid\":\"9d5d6f0b-1b9a-44ba-9406-1caaacea05de\"}],\"mode\":2}",
- "[{\"data\":\"com.discord\",\"extras\":[],\"flags\":2,\"type\":\"APP\",\"uid\":\"86c78374-59ce-4050-94a4-299f4778658f\"}]",
- "[]",
- 1,
- 0,
- "",
- 1,
- "b854ece7-2f0e-45c4-9cf3-bb5aa4fad288",
- ),
- arrayOf(
- 3,
- "{\"extras\":[],\"flags\":0,\"keys\":[],\"mode\":2}",
- "[{\"data\":\"com.google.android.vr.home\",\"extras\":[],\"flags\":2,\"type\":\"APP\",\"uid\":\"ca5b18ee-2673-4443-b2a7-cedef2c455b2\"},{\"data\":\"com.google.android.calendar\",\"extras\":[],\"flags\":0,\"type\":\"APP\",\"uid\":\"d274a5a8-f48e-4fbf-acbd-ed3c4b4ff377\"},{\"data\":\"enable_mobile_data\",\"extras\":[],\"flags\":2,\"type\":\"SYSTEM_ACTION\",\"uid\":\"f6b2afc5-4265-403d-8a0f-4eacf245286f\"}]",
- "[]",
- 1,
- 0,
- "",
- 1,
- "75ab7552-c175-4df4-9f50-1a9b86e717cc",
- ),
- )
-
- private val MIGRATION_9_10_EXPECTED_DATA = arrayOf(
- arrayOf(
- 1,
- "{\"extras\":[],\"flags\":16,\"keys\":[],\"mode\":2}",
- "[{\"data\":\"com.google.android.contacts\",\"extras\":[],\"flags\":0,\"type\":\"APP\",\"uid\":\"dc2d8c69-aaa5-4471-b981-17cba7677c1a\"}]",
- "[]",
- 1,
- 0,
- "",
- 1,
- "d314e9e8-fac9-43e7-b540-0b9c0bfb4238",
- ),
- arrayOf(
- 2,
- "{\"extras\":[],\"flags\":17,\"keys\":[{\"clickType\":0,\"deviceId\":\"io.github.sds100.keymapper.THIS_DEVICE\",\"flags\":0,\"keyCode\":25,\"uid\":\"9d5d6f0b-1b9a-44ba-9406-1caaacea05de\"}],\"mode\":2}",
- "[{\"data\":\"com.discord\",\"extras\":[],\"flags\":0,\"type\":\"APP\",\"uid\":\"86c78374-59ce-4050-94a4-299f4778658f\"}]",
- "[]",
- 1,
- 0,
- "",
- 1,
- "b854ece7-2f0e-45c4-9cf3-bb5aa4fad288",
- ),
- arrayOf(
- 3,
- "{\"extras\":[],\"flags\":16,\"keys\":[],\"mode\":2}",
- "[{\"data\":\"com.google.android.vr.home\",\"extras\":[],\"flags\":0,\"type\":\"APP\",\"uid\":\"ca5b18ee-2673-4443-b2a7-cedef2c455b2\"},{\"data\":\"com.google.android.calendar\",\"extras\":[],\"flags\":0,\"type\":\"APP\",\"uid\":\"d274a5a8-f48e-4fbf-acbd-ed3c4b4ff377\"},{\"data\":\"enable_mobile_data\",\"extras\":[],\"flags\":0,\"type\":\"SYSTEM_ACTION\",\"uid\":\"f6b2afc5-4265-403d-8a0f-4eacf245286f\"}]",
- "[]",
- 1,
- 0,
- "",
- 1,
- "75ab7552-c175-4df4-9f50-1a9b86e717cc",
- ),
- )
- }
-
- @get:Rule
- val helper: MigrationTestHelper = MigrationTestHelper(
- InstrumentationRegistry.getInstrumentation(),
- AppDatabase::class.java.canonicalName,
- FrameworkSQLiteOpenHelperFactory(),
- )
-
- private val coroutineScope = MainScope()
-
- private lateinit var jsonParser: JsonParser
- private lateinit var gson: Gson
-
- @Before
- fun init() {
- jsonParser = JsonParser()
- gson = Gson()
- }
-
- /**
- * issue #612
- */
- @Test
- @Throws(IOException::class)
- fun migrate11to12() {
- val legacyFingerprintMapsDataStore = PreferenceDataStoreFactory.create(
- corruptionHandler = null,
- migrations = emptyList(),
- scope = coroutineScope,
- produceFile = { File.createTempFile("test", ".preferences_pb") },
- )
-
- val testDataFileName = "migration-11-12-test-data.json"
- val expectedDataFileName = "migration-11-12-expected-data.json"
- val testDataJson = getJsonFileText(testDataFileName)
-
- runBlocking {
- val rootElement = jsonParser.parse(testDataJson)
-
- legacyFingerprintMapsDataStore.edit { preferences ->
- preferences.putAll(
- stringPreferencesKey("swipe_down") to gson.toJson(rootElement["fingerprint_swipe_down"]),
- stringPreferencesKey("swipe_up") to gson.toJson(rootElement["fingerprint_swipe_up"]),
- stringPreferencesKey("swipe_left") to gson.toJson(rootElement["fingerprint_swipe_left"]),
- stringPreferencesKey("swipe_right") to gson.toJson(rootElement["fingerprint_swipe_right"]),
- )
- }
- }
-
- val fromVersion = 11
- val toVersion = 12
- val migration = AppDatabase.RoomMigration11To12(legacyFingerprintMapsDataStore)
- val keyMapColumnNameToJsonNameMap = mapOf(
- "id" to "id",
- "trigger" to "trigger",
- "action_list" to "actionList",
- "constraint_list" to "constraintList",
- "constraint_mode" to "constraintMode",
- "flags" to "flags",
- "folder_name" to "folderName",
- "is_enabled" to "isEnabled",
- "uid" to "uid",
- )
- val fingerprintMapColumnNameToJsonNameMap = mapOf(
- "id" to "id",
- "action_list" to "action_list",
- "constraint_list" to "constraints",
- "constraint_mode" to "constraint_mode",
- "extras" to "extras",
- "flags" to "flags",
- "is_enabled" to "enabled",
- )
-
- // do this without using the test() method because fingerprint maps are stored differently in test file
-
- helper.createDatabase(TEST_DB_NAME, fromVersion).apply {
- val keyMapJsonArray = getKeyMapListJsonFromFile(testDataFileName)
- insertMappingListJsonIntoDatabase(
- this,
- keyMapJsonArray,
- keyMapColumnNameToJsonNameMap,
- "keymaps",
- )
-
- val deviceInfoJsonArray = jsonParser.parse(testDataJson)["device_info"].asJsonArray
-
- deviceInfoJsonArray.forEach { element ->
-
- val descriptor = element["descriptor"].asString
- val name = element["name"].asString
-
- this.execSQL(
- """
- INSERT INTO deviceinfo (descriptor, name) VALUES ('$descriptor', '$name')
- """,
- )
- }
- // dont insert test data fingerprintmaps into database because they weren't in version 11
- }
-
- val db = helper.runMigrationsAndValidate(TEST_DB_NAME, toVersion, true, migration)
-
- val expectedKeyMapJsonList =
- getKeyMapListJsonFromFile(expectedDataFileName).map { element ->
-
- val values = keyMapColumnNameToJsonNameMap.values.map { key ->
- element.convertValueToJson(key).convertJsonValueToSqlValue()
- }
-
- values.toTypedArray()
- }
-
- testColumnsMatch(db, tableName = "keymaps", expectedKeyMapJsonList.toTypedArray())
-
- val expectedFingerprintMapJsonList =
- getFingerprintMapListJsonFromFile(expectedDataFileName).map { element ->
- val values = fingerprintMapColumnNameToJsonNameMap.values.map { key ->
- element.convertValueToJson(key).convertJsonValueToSqlValue()
- }
-
- values.toTypedArray()
- }
-
- testColumnsMatch(
- db,
- tableName = "fingerprintmaps",
- expectedFingerprintMapJsonList.toTypedArray(),
- )
- }
-
- /**
- * issue #621
- */
- @Test
- @Throws(IOException::class)
- fun migrate10to11() {
- test(
- fromVersion = 10,
- toVersion = 11,
- migration = AppDatabase.MIGRATION_10_11,
- testDataFileName = "migration-10-11-test-data.json",
- expectedDataFileName = "migration-10-11-expected-data.json",
- keyMapColumnNameToJsonNameMap = mapOf(
- "id" to "id",
- "trigger" to "trigger",
- "action_list" to "actionList",
- "constraint_list" to "constraintList",
- "constraint_mode" to "constraintMode",
- "flags" to "flags",
- "folder_name" to "folderName",
- "is_enabled" to "isEnabled",
- "uid" to "uid",
- ),
- )
- }
-
- @Suppress("VARIABLE_WITH_REDUNDANT_INITIALIZER")
- @Test
- @Throws(IOException::class)
- fun migrate9to10() {
- var db = helper.createDatabase(TEST_DB_NAME, 9).apply {
- MIGRATION_9_10_TEST_DATA.forEach { row ->
-
- execSQL(
- """
- INSERT INTO keymaps (id, trigger, action_list, constraint_list, constraint_mode, flags, folder_name, is_enabled, uid)
- VALUES (${row.joinToString { "'$it'" }})
- """,
- )
- }
- close()
- }
-
- db = helper.runMigrationsAndValidate(TEST_DB_NAME, 10, true, AppDatabase.MIGRATION_9_10)
-
- testColumnsMatch(db, tableName = "keymaps", MIGRATION_9_10_EXPECTED_DATA)
- }
-
- @Suppress("VARIABLE_WITH_REDUNDANT_INITIALIZER")
- @Test
- @Throws(IOException::class)
- fun migrate5to6() {
- var db = helper.createDatabase(TEST_DB_NAME, 5).apply {
- MIGRATION_5_6_TEST_DATA.forEach { row ->
-
- execSQL(
- """
- INSERT INTO keymaps (id, trigger, action_list, constraint_list, constraint_mode, flags, folder_name, is_enabled)
- VALUES (${row.joinToString { "'$it'" }})
- """,
- )
- }
- close()
- }
-
- db = helper.runMigrationsAndValidate(TEST_DB_NAME, 6, true, AppDatabase.MIGRATION_5_6)
-
- testColumnsMatch(db, tableName = "keymaps", MIGRATION_5_6_EXPECTED_DATA)
- }
-
- @Suppress("VARIABLE_WITH_REDUNDANT_INITIALIZER")
- @Test
- @Throws(IOException::class)
- fun migrate4to5() {
- var db = helper.createDatabase(TEST_DB_NAME, 4).apply {
- MIGRATION_4_5_TEST_DATA.forEach { row ->
-
- execSQL(
- """
- INSERT INTO keymaps (id, trigger, action_list, constraint_list, constraint_mode, flags, folder_name, is_enabled)
- VALUES (${row.joinToString { "'$it'" }})
- """,
- )
- }
- close()
- }
-
- db = helper.runMigrationsAndValidate(TEST_DB_NAME, 5, true, AppDatabase.MIGRATION_4_5)
-
- testColumnsMatch(db, tableName = "keymaps", MIGRATION_4_5_EXPECTED_DATA)
- }
-
- @Suppress("VARIABLE_WITH_REDUNDANT_INITIALIZER")
- @Test
- @Throws(IOException::class)
- fun migrate3to4() {
- var db = helper.createDatabase(TEST_DB_NAME, 3).apply {
- MIGRATION_3_4_TEST_DATA.forEach { row ->
-
- execSQL(
- """
- INSERT INTO keymaps (id, trigger, action_list, constraint_list, constraint_mode, flags, folder_name, is_enabled)
- VALUES (${row.joinToString { "'$it'" }})
- """,
- )
- }
- close()
- }
-
- db = helper.runMigrationsAndValidate(TEST_DB_NAME, 4, true, AppDatabase.MIGRATION_3_4)
-
- testColumnsMatch(db, tableName = "keymaps", MIGRATION_3_4_EXPECTED_DATA)
- }
-
- @Suppress("VARIABLE_WITH_REDUNDANT_INITIALIZER")
- @Test
- @Throws(IOException::class)
- fun migrate2to3() {
- var db = helper.createDatabase(TEST_DB_NAME, 2).apply {
- MIGRATION_2_3_TEST_DATA.forEach { row ->
-
- execSQL(
- """
- INSERT INTO keymaps (id, trigger, action_list, constraint_list, constraint_mode, flags, folder_name, is_enabled)
- VALUES (${row.joinToString { "'$it'" }})
- """,
- )
- }
- close()
- }
-
- db = helper.runMigrationsAndValidate(TEST_DB_NAME, 3, true, AppDatabase.MIGRATION_2_3)
-
- testColumnsMatch(db, tableName = "keymaps", MIGRATION_2_3_EXPECTED_DATA)
- }
-
- @Suppress("VARIABLE_WITH_REDUNDANT_INITIALIZER")
- @Test
- @Throws(IOException::class)
- fun migrate1to2() {
- var db = helper.createDatabase(TEST_DB_NAME, 1).apply {
- MIGRATION_1_2_TEST_DATA.forEach { row ->
-
- execSQL(
- """
- INSERT INTO keymaps (id, trigger_list, flags, is_enabled, action_type, action_data, action_extras)
- VALUES (${row.joinToString { "'$it'" }})
- """,
- )
- }
-
- close()
- }
-
- db = helper.runMigrationsAndValidate(TEST_DB_NAME, 2, true, AppDatabase.MIGRATION_1_2)
- testColumnsMatch(db, tableName = "keymaps", MIGRATION_1_2_EXPECTED_DATA)
- }
-
- private fun test(
- fromVersion: Int,
- toVersion: Int,
- migration: Migration,
- testDataFileName: String,
- expectedDataFileName: String,
- keyMapColumnNameToJsonNameMap: Map,
- fingerprintMapColumnNameToJsonNameMap: Map? = null,
- ) {
- helper.createDatabase(TEST_DB_NAME, fromVersion).apply {
- val keyMapJsonArray = getKeyMapListJsonFromFile(testDataFileName)
- insertMappingListJsonIntoDatabase(
- this,
- keyMapJsonArray,
- keyMapColumnNameToJsonNameMap,
- "keymaps",
- )
-
- if (fingerprintMapColumnNameToJsonNameMap != null) {
- val fingerprintMapJsonArray = getFingerprintMapListJsonFromFile(testDataFileName)
- insertMappingListJsonIntoDatabase(
- this,
- fingerprintMapJsonArray,
- fingerprintMapColumnNameToJsonNameMap,
- "fingerprintmaps",
- )
- }
- }
-
- val db = helper.runMigrationsAndValidate(TEST_DB_NAME, toVersion, true, migration)
-
- val expectedKeyMapJsonList =
- getKeyMapListJsonFromFile(expectedDataFileName).map { element ->
-
- val values = keyMapColumnNameToJsonNameMap.values.map { key ->
- element.convertValueToJson(key).convertJsonValueToSqlValue()
- }
-
- values.toTypedArray()
- }
-
- testColumnsMatch(db, tableName = "keymaps", expectedKeyMapJsonList.toTypedArray())
-
- if (fingerprintMapColumnNameToJsonNameMap != null) {
- val expectedFingerprintMapJsonList =
- getFingerprintMapListJsonFromFile(expectedDataFileName).map { element ->
- val values = fingerprintMapColumnNameToJsonNameMap.values.map { key ->
- element.convertValueToJson(key).convertJsonValueToSqlValue()
- }
-
- values.toTypedArray()
- }
-
- testColumnsMatch(
- db,
- tableName = "fingerprintmaps",
- expectedFingerprintMapJsonList.toTypedArray(),
- )
- }
- }
-
- private fun insertMappingListJsonIntoDatabase(
- database: SupportSQLiteDatabase,
- mappingListJson: JsonArray,
- columnNameToJsonMap: Map,
- tableName: String,
- ) {
- mappingListJson.forEach { element ->
- val jsonElementNames = columnNameToJsonMap.values
-
- val values = jsonElementNames.map { key ->
- element.convertValueToJson(key).convertJsonValueToSqlValue()
- }
-
- database.execSQL(
- """
- INSERT INTO $tableName (${columnNameToJsonMap.keys.joinToString()})
- VALUES (${values.joinToString { "'$it'" }})
- """,
- )
- }
- }
-
- private fun String?.convertJsonValueToSqlValue() =
- when {
- this == null -> "NULL"
- this == "true" -> 1
- this == "false" -> 0
- this.toIntOrNull() != null -> this.toInt()
- else -> this
- }
-
- private fun JsonElement.convertValueToJson(key: String) = try {
- gson.toJson(this[key])
- } catch (e: NoSuchElementException) {
- null
- }
-
- private fun getKeyMapListJsonFromFile(fileName: String): JsonArray {
- val json = getJsonFileText(fileName)
-
- val rootElement = jsonParser.parse(json)
-
- return rootElement["keymap_list"].asJsonArray
- }
-
- private fun getFingerprintMapListJsonFromFile(fileName: String): JsonArray {
- val json = getJsonFileText(fileName)
-
- val rootElement = jsonParser.parse(json)
-
- return rootElement["fingerprint_map_list"].asJsonArray
- }
-
- private fun getJsonFileText(fileName: String): String {
- val inputStream =
- this.javaClass.classLoader!!.getResourceAsStream("json-migration-test/$fileName")
- return inputStream.bufferedReader().use { it.readText() }
- }
-
- private fun testColumnsMatch(
- db: SupportSQLiteDatabase,
- tableName: String,
- expectedData: Array>,
- ) {
- val cursor = db.query("SELECT * FROM $tableName")
-
- ViewMatchers.assertThat("Check the logcat", cursor.count, Matchers.`is`(expectedData.size))
-
- while (cursor.moveToNext()) {
- val row = cursor.position
- val expectedColumnValues: Array = expectedData[row]
-
- cursor.columnNames.forEachIndexed { columnIndex, columnName ->
-
- val expectedColumnValue = expectedColumnValues[columnIndex]
-
- val columnValue: Any = when (expectedColumnValue) {
- is Int -> cursor.getInt(columnIndex)
- is String -> cursor.getString(columnIndex)
- else -> throw Exception("Don't know how to get this type ${expectedColumnValue::class.simpleName} from cursor")
- }
-
- when (expectedColumnValue) {
- is Int -> ViewMatchers.assertThat(
- "$columnName at row $row doesn't match",
- columnValue,
- Matchers.`is`(expectedColumnValue),
- )
- is String -> {
- try {
- JsonTestUtils.compareBothWays(
- element = jsonParser.parse(expectedColumnValue),
- elementName = "expected $columnName at row $row",
- other = jsonParser.parse(columnValue as String),
- otherName = "migrated $columnName at row $row",
- )
- } catch (e: JsonParseException) {
- ViewMatchers.assertThat(
- "$columnName at row $row doesn't match",
- columnValue,
- Matchers.`is`(expectedColumnValue),
- )
- }
- }
- }
- }
- }
- }
-}
diff --git a/app/src/debug/res/values/strings.xml b/app/src/debug/res/values/strings.xml
index 0e97b14435..bc42450e26 100644
--- a/app/src/debug/res/values/strings.xml
+++ b/app/src/debug/res/values/strings.xml
@@ -1,5 +1,5 @@
- Key Mapper Debug
+ Key Mapper Debug
Key Mapper Debug Basic Input Method
\ No newline at end of file
diff --git a/app/src/free/java/io/github/sds100/keymapper/MainActivity.kt b/app/src/free/java/io/github/sds100/keymapper/MainActivity.kt
deleted file mode 100644
index 51dddd9b2e..0000000000
--- a/app/src/free/java/io/github/sds100/keymapper/MainActivity.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package io.github.sds100.keymapper
-
-class MainActivity : BaseMainActivity()
diff --git a/app/src/free/java/io/github/sds100/keymapper/floating/FloatingLayoutsScreen.kt b/app/src/free/java/io/github/sds100/keymapper/floating/FloatingLayoutsScreen.kt
deleted file mode 100644
index ed0f903e9e..0000000000
--- a/app/src/free/java/io/github/sds100/keymapper/floating/FloatingLayoutsScreen.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package io.github.sds100.keymapper.floating
-
-import androidx.compose.foundation.lazy.LazyListState
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.navigation.NavHostController
-
-@Composable
-fun FloatingLayoutsScreen(
- modifier: Modifier = Modifier,
- viewModel: ListFloatingLayoutsViewModel,
- navController: NavHostController,
- lazyListState: LazyListState,
-) {
-}
diff --git a/app/src/free/java/io/github/sds100/keymapper/floating/ListFloatingLayoutsUseCase.kt b/app/src/free/java/io/github/sds100/keymapper/floating/ListFloatingLayoutsUseCase.kt
deleted file mode 100644
index 5a1a8786de..0000000000
--- a/app/src/free/java/io/github/sds100/keymapper/floating/ListFloatingLayoutsUseCase.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package io.github.sds100.keymapper.floating
-
-import io.github.sds100.keymapper.data.repositories.FloatingLayoutRepository
-import io.github.sds100.keymapper.data.repositories.PreferenceRepository
-import io.github.sds100.keymapper.purchasing.PurchasingManager
-import io.github.sds100.keymapper.system.accessibility.ServiceAdapter
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
-
-interface ListFloatingLayoutsUseCase {
- val showFloatingLayouts: Flow
-}
-
-class ListFloatingLayoutsUseCaseImpl(
- private val repository: FloatingLayoutRepository,
- private val purchasingManager: PurchasingManager,
- private val serviceAdapter: ServiceAdapter,
- private val preferences: PreferenceRepository,
-) : ListFloatingLayoutsUseCase,
- PurchasingManager by purchasingManager {
-
- override val showFloatingLayouts: Flow = flowOf(false)
-}
diff --git a/app/src/free/java/io/github/sds100/keymapper/floating/ListFloatingLayoutsViewModel.kt b/app/src/free/java/io/github/sds100/keymapper/floating/ListFloatingLayoutsViewModel.kt
deleted file mode 100644
index f3e8a9dd9a..0000000000
--- a/app/src/free/java/io/github/sds100/keymapper/floating/ListFloatingLayoutsViewModel.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package io.github.sds100.keymapper.floating
-
-import io.github.sds100.keymapper.util.ui.PopupViewModel
-import io.github.sds100.keymapper.util.ui.PopupViewModelImpl
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-
-class ListFloatingLayoutsViewModel(
- val coroutineScope: CoroutineScope,
- val useCase: ListFloatingLayoutsUseCase,
- resourceProvider: ResourceProvider,
-) : PopupViewModel by PopupViewModelImpl() {
- val state: StateFlow = MutableStateFlow(FloatingLayoutsState.NotPurchased)
- val showFabText: Boolean = false
-
- fun onNewLayoutClick() {
- }
-}
-
-sealed class FloatingLayoutsState {
- data object Loading : FloatingLayoutsState()
- data object NotPurchased : FloatingLayoutsState()
- data object Purchased : FloatingLayoutsState()
-}
diff --git a/app/src/free/java/io/github/sds100/keymapper/home/HomeFloatingLayoutsScreen.kt b/app/src/free/java/io/github/sds100/keymapper/home/HomeFloatingLayoutsScreen.kt
deleted file mode 100644
index aca43b669c..0000000000
--- a/app/src/free/java/io/github/sds100/keymapper/home/HomeFloatingLayoutsScreen.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package io.github.sds100.keymapper.home
-
-import androidx.compose.material3.SnackbarHostState
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.Dp
-import androidx.navigation.NavHostController
-import io.github.sds100.keymapper.floating.ListFloatingLayoutsViewModel
-
-@Composable
-fun HomeFloatingLayoutsScreen(
- modifier: Modifier = Modifier,
- viewModel: ListFloatingLayoutsViewModel,
- navController: NavHostController,
- snackbarState: SnackbarHostState,
- fabBottomPadding: Dp,
-) {
-}
diff --git a/app/src/free/java/io/github/sds100/keymapper/purchasing/PurchasingManagerImpl.kt b/app/src/free/java/io/github/sds100/keymapper/purchasing/PurchasingManagerImpl.kt
deleted file mode 100644
index 88a2d9a5ee..0000000000
--- a/app/src/free/java/io/github/sds100/keymapper/purchasing/PurchasingManagerImpl.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package io.github.sds100.keymapper.purchasing
-
-import android.content.Context
-import io.github.sds100.keymapper.util.Error
-import io.github.sds100.keymapper.util.Result
-import io.github.sds100.keymapper.util.State
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.MutableStateFlow
-
-class PurchasingManagerImpl(
- context: Context,
- private val coroutineScope: CoroutineScope,
-) : PurchasingManager {
- override val onCompleteProductPurchase: MutableSharedFlow = MutableSharedFlow()
- override val purchases: Flow>>> =
- MutableStateFlow(State.Data(Error.PurchasingNotImplemented))
-
- override suspend fun launchPurchasingFlow(product: ProductId): Result {
- return Error.PurchasingNotImplemented
- }
-
- override suspend fun getProductPrice(product: ProductId): Result {
- return Error.PurchasingNotImplemented
- }
-
- override suspend fun isPurchased(product: ProductId): Result {
- return Error.PurchasingNotImplemented
- }
-
- override fun refresh() {}
-}
diff --git a/app/src/free/java/io/github/sds100/keymapper/system/accessibility/AccessibilityServiceController.kt b/app/src/free/java/io/github/sds100/keymapper/system/accessibility/AccessibilityServiceController.kt
deleted file mode 100644
index 44c0fcc963..0000000000
--- a/app/src/free/java/io/github/sds100/keymapper/system/accessibility/AccessibilityServiceController.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-package io.github.sds100.keymapper.system.accessibility
-
-import io.github.sds100.keymapper.actions.PerformActionsUseCase
-import io.github.sds100.keymapper.constraints.DetectConstraintsUseCase
-import io.github.sds100.keymapper.data.repositories.AccessibilityNodeRepository
-import io.github.sds100.keymapper.data.repositories.PreferenceRepository
-import io.github.sds100.keymapper.keymaps.FingerprintGesturesSupportedUseCase
-import io.github.sds100.keymapper.keymaps.PauseKeyMapsUseCase
-import io.github.sds100.keymapper.keymaps.detection.DetectKeyMapsUseCase
-import io.github.sds100.keymapper.reroutekeyevents.RerouteKeyEventsUseCase
-import io.github.sds100.keymapper.system.devices.DevicesAdapter
-import io.github.sds100.keymapper.system.inputmethod.InputMethodAdapter
-import io.github.sds100.keymapper.system.root.SuAdapter
-import io.github.sds100.keymapper.util.ServiceEvent
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.SharedFlow
-
-class AccessibilityServiceController(
- coroutineScope: CoroutineScope,
- accessibilityService: MyAccessibilityService,
- inputEvents: SharedFlow,
- outputEvents: MutableSharedFlow,
- detectConstraintsUseCase: DetectConstraintsUseCase,
- performActionsUseCase: PerformActionsUseCase,
- detectKeyMapsUseCase: DetectKeyMapsUseCase,
- fingerprintGesturesSupportedUseCase: FingerprintGesturesSupportedUseCase,
- rerouteKeyEventsUseCase: RerouteKeyEventsUseCase,
- pauseKeyMapsUseCase: PauseKeyMapsUseCase,
- devicesAdapter: DevicesAdapter,
- suAdapter: SuAdapter,
- inputMethodAdapter: InputMethodAdapter,
- settingsRepository: PreferenceRepository,
- nodeRepository: AccessibilityNodeRepository,
-) : BaseAccessibilityServiceController(
- coroutineScope,
- accessibilityService,
- inputEvents,
- outputEvents,
- detectConstraintsUseCase,
- performActionsUseCase,
- detectKeyMapsUseCase,
- fingerprintGesturesSupportedUseCase,
- rerouteKeyEventsUseCase,
- pauseKeyMapsUseCase,
- devicesAdapter,
- suAdapter,
- inputMethodAdapter,
- settingsRepository,
- nodeRepository,
-)
diff --git a/app/src/free/java/io/github/sds100/keymapper/trigger/AssistantTriggerSetupBottomSheet.kt b/app/src/free/java/io/github/sds100/keymapper/trigger/AssistantTriggerSetupBottomSheet.kt
deleted file mode 100644
index 8c3c7cbd59..0000000000
--- a/app/src/free/java/io/github/sds100/keymapper/trigger/AssistantTriggerSetupBottomSheet.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package io.github.sds100.keymapper.trigger
-
-import androidx.compose.runtime.Composable
-
-@Composable
-fun HandleAssistantTriggerSetupBottomSheet(
- viewModel: ConfigTriggerViewModel,
-) {
-}
diff --git a/app/src/free/java/io/github/sds100/keymapper/trigger/ConfigTriggerViewModel.kt b/app/src/free/java/io/github/sds100/keymapper/trigger/ConfigTriggerViewModel.kt
deleted file mode 100644
index 89a8da1b07..0000000000
--- a/app/src/free/java/io/github/sds100/keymapper/trigger/ConfigTriggerViewModel.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package io.github.sds100.keymapper.trigger
-
-import io.github.sds100.keymapper.keymaps.ConfigKeyMapUseCase
-import io.github.sds100.keymapper.keymaps.CreateKeyMapShortcutUseCase
-import io.github.sds100.keymapper.keymaps.DisplayKeyMapUseCase
-import io.github.sds100.keymapper.keymaps.FingerprintGesturesSupportedUseCase
-import io.github.sds100.keymapper.onboarding.OnboardingUseCase
-import io.github.sds100.keymapper.purchasing.PurchasingManager
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-import kotlinx.coroutines.CoroutineScope
-
-class ConfigTriggerViewModel(
- coroutineScope: CoroutineScope,
- onboarding: OnboardingUseCase,
- config: ConfigKeyMapUseCase,
- recordTrigger: RecordTriggerUseCase,
- createKeyMapShortcut: CreateKeyMapShortcutUseCase,
- displayKeyMap: DisplayKeyMapUseCase,
- resourceProvider: ResourceProvider,
- purchasingManager: PurchasingManager,
- setupGuiKeyboardUseCase: SetupGuiKeyboardUseCase,
- fingerprintGesturesSupported: FingerprintGesturesSupportedUseCase,
-) : BaseConfigTriggerViewModel(
- coroutineScope,
- onboarding,
- config,
- recordTrigger,
- createKeyMapShortcut,
- displayKeyMap,
- purchasingManager,
- setupGuiKeyboardUseCase,
- fingerprintGesturesSupported,
- resourceProvider,
-) {
- fun onEditFloatingButtonClick() {}
- fun onEditFloatingLayoutClick() {}
-}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1f8d932dfb..fd3a99ee47 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,78 +2,13 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ tools:ignore="GoogleAppIndexingWarning,MissingTvBanner">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/java/io/github/sds100/keymapper/ActivityViewModel.kt b/app/src/main/java/io/github/sds100/keymapper/ActivityViewModel.kt
deleted file mode 100644
index 8fefdddbe4..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/ActivityViewModel.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package io.github.sds100.keymapper
-
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.viewModelScope
-import io.github.sds100.keymapper.util.ui.NavigationViewModel
-import io.github.sds100.keymapper.util.ui.NavigationViewModelImpl
-import io.github.sds100.keymapper.util.ui.PopupViewModel
-import io.github.sds100.keymapper.util.ui.PopupViewModelImpl
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-import io.github.sds100.keymapper.util.ui.ViewModelHelper
-import kotlinx.coroutines.launch
-
-/**
- * Created by sds100 on 23/07/2021.
- */
-class ActivityViewModel(
- resourceProvider: ResourceProvider,
-) : ViewModel(),
- ResourceProvider by resourceProvider,
- PopupViewModel by PopupViewModelImpl(),
- NavigationViewModel by NavigationViewModelImpl() {
-
- var handledActivityLaunchIntent: Boolean = false
- var previousNightMode: Int? = null
-
- fun onCantFindAccessibilitySettings() {
- viewModelScope.launch {
- ViewModelHelper.handleCantFindAccessibilitySettings(
- resourceProvider = this@ActivityViewModel,
- popupViewModel = this@ActivityViewModel,
- )
- }
- }
-
- @Suppress("UNCHECKED_CAST")
- class Factory(
- private val resourceProvider: ResourceProvider,
- ) : ViewModelProvider.NewInstanceFactory() {
-
- override fun create(modelClass: Class): T =
- ActivityViewModel(resourceProvider) as T
- }
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/AppHiltModule.kt b/app/src/main/java/io/github/sds100/keymapper/AppHiltModule.kt
new file mode 100644
index 0000000000..286adf22ad
--- /dev/null
+++ b/app/src/main/java/io/github/sds100/keymapper/AppHiltModule.kt
@@ -0,0 +1,60 @@
+package io.github.sds100.keymapper
+
+import android.os.Build
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import io.github.sds100.keymapper.base.purchasing.PurchasingManager
+import io.github.sds100.keymapper.common.BuildConfigProvider
+import io.github.sds100.keymapper.common.KeyMapperClassProvider
+import io.github.sds100.keymapper.common.utils.DefaultDispatcherProvider
+import io.github.sds100.keymapper.common.utils.DispatcherProvider
+import io.github.sds100.keymapper.purchasing.PurchasingManagerImpl
+import io.github.sds100.keymapper.system.accessibility.MyAccessibilityService
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+class AppHiltModule {
+ @Singleton
+ @Provides
+ fun provideCoroutineScope(): CoroutineScope = MainScope()
+
+ @Provides
+ @Singleton
+ fun provideDispatchers(): DispatcherProvider = DefaultDispatcherProvider()
+
+ @Singleton
+ @Provides
+ fun provideBuildConfigProvider(): BuildConfigProvider = object : BuildConfigProvider {
+ override val minApi: Int
+ get() = Build.VERSION_CODES.LOLLIPOP
+ override val maxApi: Int
+ get() = 1000
+ override val packageName: String
+ get() = BuildConfig.APPLICATION_ID
+ override val version: String
+ get() = BuildConfig.VERSION_NAME
+ override val versionCode: Int
+ get() = BuildConfig.VERSION_CODE
+ }
+
+ @Singleton
+ @Provides
+ fun provideClassProvider(): KeyMapperClassProvider = object : KeyMapperClassProvider {
+ override fun getMainActivity(): Class<*> {
+ return MainActivity::class.java
+ }
+
+ override fun getAccessibilityService(): Class<*> {
+ return MyAccessibilityService::class.java
+ }
+ }
+
+ @Provides
+ @Singleton
+ fun providePurchasingManager(): PurchasingManager = PurchasingManagerImpl()
+}
diff --git a/app/src/main/java/io/github/sds100/keymapper/Constants.kt b/app/src/main/java/io/github/sds100/keymapper/Constants.kt
deleted file mode 100644
index 7ba1f736d8..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/Constants.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package io.github.sds100.keymapper
-
-import android.os.Build
-
-/**
- * Created by sds100 on 22/11/2018.
- */
-object Constants {
- const val MIN_API = Build.VERSION_CODES.LOLLIPOP
- const val MAX_API = 1000
- const val PACKAGE_NAME = BuildConfig.APPLICATION_ID
- const val VERSION = BuildConfig.VERSION_NAME
- const val VERSION_CODE = BuildConfig.VERSION_CODE
- const val MIN_API_FLOATING_BUTTONS = Build.VERSION_CODES.R
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/KeyMapperApp.kt b/app/src/main/java/io/github/sds100/keymapper/KeyMapperApp.kt
index 5519d434ce..eb29f31ee0 100644
--- a/app/src/main/java/io/github/sds100/keymapper/KeyMapperApp.kt
+++ b/app/src/main/java/io/github/sds100/keymapper/KeyMapperApp.kt
@@ -1,320 +1,13 @@
package io.github.sds100.keymapper
import android.annotation.SuppressLint
-import android.content.Intent
-import android.os.Build
-import android.os.UserManager
-import android.util.Log
-import androidx.appcompat.app.AppCompatDelegate
-import androidx.core.content.getSystemService
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleObserver
-import androidx.lifecycle.OnLifecycleEvent
-import androidx.lifecycle.ProcessLifecycleOwner
-import androidx.multidex.MultiDexApplication
-import io.github.sds100.keymapper.actions.uielement.InteractUiElementController
-import io.github.sds100.keymapper.data.Keys
-import io.github.sds100.keymapper.data.entities.LogEntryEntity
-import io.github.sds100.keymapper.logging.KeyMapperLoggingTree
-import io.github.sds100.keymapper.purchasing.PurchasingManagerImpl
-import io.github.sds100.keymapper.settings.ThemeUtils
-import io.github.sds100.keymapper.shizuku.ShizukuAdapterImpl
-import io.github.sds100.keymapper.system.AndroidSystemFeatureAdapter
-import io.github.sds100.keymapper.system.accessibility.AccessibilityServiceAdapter
-import io.github.sds100.keymapper.system.airplanemode.AndroidAirplaneModeAdapter
-import io.github.sds100.keymapper.system.apps.AndroidAppShortcutAdapter
-import io.github.sds100.keymapper.system.apps.AndroidPackageManagerAdapter
-import io.github.sds100.keymapper.system.bluetooth.AndroidBluetoothAdapter
-import io.github.sds100.keymapper.system.camera.AndroidCameraAdapter
-import io.github.sds100.keymapper.system.clipboard.AndroidClipboardAdapter
-import io.github.sds100.keymapper.system.devices.AndroidDevicesAdapter
-import io.github.sds100.keymapper.system.display.AndroidDisplayAdapter
-import io.github.sds100.keymapper.system.files.AndroidFileAdapter
-import io.github.sds100.keymapper.system.inputmethod.AndroidInputMethodAdapter
-import io.github.sds100.keymapper.system.inputmethod.AutoSwitchImeController
-import io.github.sds100.keymapper.system.inputmethod.ShowHideInputMethodUseCaseImpl
-import io.github.sds100.keymapper.system.intents.IntentAdapterImpl
-import io.github.sds100.keymapper.system.leanback.LeanbackAdapterImpl
-import io.github.sds100.keymapper.system.lock.AndroidLockScreenAdapter
-import io.github.sds100.keymapper.system.media.AndroidMediaAdapter
-import io.github.sds100.keymapper.system.network.AndroidNetworkAdapter
-import io.github.sds100.keymapper.system.nfc.AndroidNfcAdapter
-import io.github.sds100.keymapper.system.notifications.AndroidNotificationAdapter
-import io.github.sds100.keymapper.system.notifications.ManageNotificationsUseCaseImpl
-import io.github.sds100.keymapper.system.notifications.NotificationController
-import io.github.sds100.keymapper.system.notifications.NotificationReceiverAdapter
-import io.github.sds100.keymapper.system.permissions.AndroidPermissionAdapter
-import io.github.sds100.keymapper.system.permissions.AutoGrantPermissionController
-import io.github.sds100.keymapper.system.permissions.Permission
-import io.github.sds100.keymapper.system.phone.AndroidPhoneAdapter
-import io.github.sds100.keymapper.system.popup.AndroidToastAdapter
-import io.github.sds100.keymapper.system.power.AndroidPowerAdapter
-import io.github.sds100.keymapper.system.ringtones.AndroidRingtoneAdapter
-import io.github.sds100.keymapper.system.root.SuAdapterImpl
-import io.github.sds100.keymapper.system.url.AndroidOpenUrlAdapter
-import io.github.sds100.keymapper.system.vibrator.AndroidVibratorAdapter
-import io.github.sds100.keymapper.system.volume.AndroidVolumeAdapter
-import io.github.sds100.keymapper.trigger.RecordTriggerController
-import io.github.sds100.keymapper.util.ui.ResourceProviderImpl
-import kotlinx.coroutines.MainScope
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
-import splitties.toast.toast
-import timber.log.Timber
-import java.util.Calendar
+import dagger.hilt.android.HiltAndroidApp
+import io.github.sds100.keymapper.base.BaseKeyMapperApp
-/**
- * Created by sds100 on 19/05/2020.
- */
@SuppressLint("LogNotTimber")
-class KeyMapperApp : MultiDexApplication() {
- private val tag = KeyMapperApp::class.simpleName
-
- val appCoroutineScope = MainScope()
-
- val notificationAdapter by lazy { AndroidNotificationAdapter(this, appCoroutineScope) }
-
- lateinit var notificationController: NotificationController
- lateinit var autoSwitchImeController: AutoSwitchImeController
-
- val resourceProvider by lazy { ResourceProviderImpl(this, appCoroutineScope) }
-
- val bluetoothMonitor by lazy { AndroidBluetoothAdapter(this, appCoroutineScope) }
-
- val packageManagerAdapter by lazy {
- AndroidPackageManagerAdapter(
- this,
- appCoroutineScope,
- )
- }
-
- val inputMethodAdapter by lazy {
- AndroidInputMethodAdapter(
- this,
- appCoroutineScope,
- accessibilityServiceAdapter,
- permissionAdapter,
- suAdapter,
- )
- }
- val devicesAdapter by lazy {
- AndroidDevicesAdapter(
- this,
- bluetoothMonitor,
- permissionAdapter,
- appCoroutineScope,
- )
- }
- val cameraAdapter by lazy { AndroidCameraAdapter(this) }
- val permissionAdapter by lazy {
- AndroidPermissionAdapter(
- this,
- appCoroutineScope,
- suAdapter,
- notificationReceiverAdapter,
- ServiceLocator.settingsRepository(this),
- packageManagerAdapter,
- )
- }
-
- val systemFeatureAdapter by lazy { AndroidSystemFeatureAdapter(this) }
- val accessibilityServiceAdapter by lazy { AccessibilityServiceAdapter(this, appCoroutineScope) }
- val notificationReceiverAdapter by lazy { NotificationReceiverAdapter(this, appCoroutineScope) }
- val appShortcutAdapter by lazy { AndroidAppShortcutAdapter(this) }
- val fileAdapter by lazy { AndroidFileAdapter(this) }
- val popupMessageAdapter by lazy { AndroidToastAdapter(this) }
- val vibratorAdapter by lazy { AndroidVibratorAdapter(this) }
- val displayAdapter by lazy { AndroidDisplayAdapter(this, coroutineScope = appCoroutineScope) }
- val audioAdapter by lazy { AndroidVolumeAdapter(this) }
- val suAdapter by lazy {
- SuAdapterImpl(
- appCoroutineScope,
- ServiceLocator.settingsRepository(this),
- )
- }
- val phoneAdapter by lazy { AndroidPhoneAdapter(this, appCoroutineScope) }
- val intentAdapter by lazy { IntentAdapterImpl(this) }
- val mediaAdapter by lazy { AndroidMediaAdapter(this, appCoroutineScope) }
- val lockScreenAdapter by lazy { AndroidLockScreenAdapter(this) }
- val airplaneModeAdapter by lazy { AndroidAirplaneModeAdapter(this, suAdapter) }
- val networkAdapter by lazy { AndroidNetworkAdapter(this, suAdapter) }
- val nfcAdapter by lazy { AndroidNfcAdapter(this, suAdapter) }
- val openUrlAdapter by lazy { AndroidOpenUrlAdapter(this) }
- val clipboardAdapter by lazy { AndroidClipboardAdapter(this) }
- val shizukuAdapter by lazy { ShizukuAdapterImpl(appCoroutineScope, packageManagerAdapter) }
- val leanbackAdapter by lazy { LeanbackAdapterImpl(this) }
- val powerAdapter by lazy { AndroidPowerAdapter(this) }
-
- val recordTriggerController by lazy {
- RecordTriggerController(appCoroutineScope, accessibilityServiceAdapter)
- }
-
- val interactUiElementController by lazy {
- InteractUiElementController(
- appCoroutineScope,
- accessibilityServiceAdapter,
- ServiceLocator.accessibilityNodeRepository(this),
- packageManagerAdapter,
- )
- }
-
- val autoGrantPermissionController by lazy {
- AutoGrantPermissionController(
- appCoroutineScope,
- permissionAdapter,
- popupMessageAdapter,
- resourceProvider,
- )
- }
-
- val purchasingManager: PurchasingManagerImpl by lazy {
- PurchasingManagerImpl(this.applicationContext, appCoroutineScope)
- }
-
- val ringtoneManagerAdapter: AndroidRingtoneAdapter by lazy {
- AndroidRingtoneAdapter(this)
- }
-
- private val loggingTree by lazy {
- KeyMapperLoggingTree(
- appCoroutineScope,
- ServiceLocator.settingsRepository(this),
- ServiceLocator.logRepository(this),
- )
- }
-
- private val processLifecycleOwner by lazy { ProcessLifecycleOwner.get() }
-
- private val userManager: UserManager? by lazy { getSystemService() }
-
- private val initLock: Any = Any()
- private var initialized = false
-
- override fun onCreate() {
- val priorExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
-
- 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 {
- ServiceLocator.logRepository(this@KeyMapperApp).insertSuspend(entry)
- }
-
- priorExceptionHandler?.uncaughtException(thread, exception)
- }
-
- super.onCreate()
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && userManager?.isUserUnlocked == false) {
- Log.i(tag, "KeyMapperApp: Delay init because locked.")
- // If the device is still encrypted and locked do not initialize anything that
- // may potentially need the encrypted app storage like databases.
- return
- }
-
- synchronized(initLock) {
- init()
- initialized = true
- }
- }
-
- fun onBootUnlocked() {
- synchronized(initLock) {
- if (!initialized) {
- init()
- }
- initialized = true
- }
- }
-
- private fun init() {
- Log.i(tag, "KeyMapperApp: Init")
-
- ServiceLocator.settingsRepository(this).get(Keys.darkTheme)
- .map { it?.toIntOrNull() }
- .map {
- when (it) {
- ThemeUtils.DARK -> AppCompatDelegate.MODE_NIGHT_YES
- ThemeUtils.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
- else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
- }
- }
- .onEach { mode -> AppCompatDelegate.setDefaultNightMode(mode) }
- .launchIn(appCoroutineScope)
-
- if (BuildConfig.BUILD_TYPE == "debug" || BuildConfig.BUILD_TYPE == "debug_release") {
- Timber.plant(Timber.DebugTree())
- }
-
- Timber.plant(loggingTree)
-
- notificationController = NotificationController(
- appCoroutineScope,
- ManageNotificationsUseCaseImpl(
- ServiceLocator.settingsRepository(this),
- notificationAdapter,
- suAdapter,
- permissionAdapter,
- ),
- UseCases.pauseKeyMaps(this),
- UseCases.showImePicker(this),
- UseCases.controlAccessibilityService(this),
- UseCases.toggleCompatibleIme(this),
- ShowHideInputMethodUseCaseImpl(ServiceLocator.accessibilityServiceAdapter(this)),
- UseCases.onboarding(this),
- ServiceLocator.resourceProvider(this),
- )
-
- autoSwitchImeController = AutoSwitchImeController(
- appCoroutineScope,
- ServiceLocator.settingsRepository(this),
- ServiceLocator.inputMethodAdapter(this),
- UseCases.pauseKeyMaps(this),
- devicesAdapter,
- popupMessageAdapter,
- resourceProvider,
- ServiceLocator.accessibilityServiceAdapter(this),
- )
-
- processLifecycleOwner.lifecycle.addObserver(object : LifecycleObserver {
- @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()
-
- if (BuildConfig.DEBUG && permissionAdapter.isGranted(Permission.WRITE_SECURE_SETTINGS)) {
- accessibilityServiceAdapter.start()
- }
- }
- })
-
- appCoroutineScope.launch {
- notificationController.openApp.collectLatest { intentAction ->
- Intent(this@KeyMapperApp, MainActivity::class.java).apply {
- action = intentAction
- flags = Intent.FLAG_ACTIVITY_NEW_TASK
-
- startActivity(this)
- }
- }
- }
-
- notificationController.showToast.onEach {
- toast(it)
- }.launchIn(appCoroutineScope)
-
- autoGrantPermissionController.start()
+@HiltAndroidApp
+class KeyMapperApp : BaseKeyMapperApp() {
+ override fun getMainActivityClass(): Class<*> {
+ return MainActivity::class.java
}
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/MainActivity.kt b/app/src/main/java/io/github/sds100/keymapper/MainActivity.kt
new file mode 100644
index 0000000000..d48551a3dc
--- /dev/null
+++ b/app/src/main/java/io/github/sds100/keymapper/MainActivity.kt
@@ -0,0 +1,43 @@
+package io.github.sds100.keymapper
+
+import android.os.Bundle
+import androidx.databinding.DataBindingUtil
+import androidx.navigation.fragment.FragmentNavigator
+import androidx.navigation.fragment.NavHostFragment
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.sds100.keymapper.base.BaseMainActivity
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.databinding.ActivityMainBinding
+import io.github.sds100.keymapper.base.utils.ui.DialogProvider
+import io.github.sds100.keymapper.base.utils.ui.showDialogs
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class MainActivity : BaseMainActivity() {
+
+ @Inject
+ lateinit var dialogProvider: DialogProvider
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val binding =
+ DataBindingUtil.setContentView(this, R.layout.activity_main)
+
+ val navController = binding.container.getFragment().navController
+ val fragmentNavigator =
+ navController.navigatorProvider.getNavigator(FragmentNavigator::class.java)
+
+ val homeDest = fragmentNavigator.createDestination().apply {
+ id = R.id.home_fragment
+ setClassName(MainFragment::class.java.name)
+ }
+
+ navController.graph = navController.navInflater.inflate(R.navigation.nav_base_app).apply {
+ addDestination(homeDest)
+ setStartDestination(R.id.home_fragment)
+ }
+
+ dialogProvider.showDialogs(this, binding.coordinatorLayout)
+ }
+}
diff --git a/app/src/main/java/io/github/sds100/keymapper/MainFragment.kt b/app/src/main/java/io/github/sds100/keymapper/MainFragment.kt
new file mode 100644
index 0000000000..ccd1e61eee
--- /dev/null
+++ b/app/src/main/java/io/github/sds100/keymapper/MainFragment.kt
@@ -0,0 +1,152 @@
+package io.github.sds100.keymapper
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.WindowInsetsSides
+import androidx.compose.foundation.layout.add
+import androidx.compose.foundation.layout.displayCutout
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.only
+import androidx.compose.foundation.layout.systemBars
+import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.compose.ui.unit.dp
+import androidx.fragment.app.Fragment
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.sds100.keymapper.base.BaseMainNavHost
+import io.github.sds100.keymapper.base.actions.ChooseActionScreen
+import io.github.sds100.keymapper.base.actions.ChooseActionViewModel
+import io.github.sds100.keymapper.base.compose.KeyMapperTheme
+import io.github.sds100.keymapper.base.databinding.FragmentComposeBinding
+import io.github.sds100.keymapper.base.home.HomeKeyMapListScreen
+import io.github.sds100.keymapper.base.utils.navigation.NavDestination
+import io.github.sds100.keymapper.base.utils.navigation.NavigationProviderImpl
+import io.github.sds100.keymapper.base.utils.navigation.SetupNavigation
+import io.github.sds100.keymapper.base.utils.navigation.handleRouteArgs
+import io.github.sds100.keymapper.base.utils.navigation.setupFragmentNavigation
+import io.github.sds100.keymapper.base.utils.ui.DialogProviderImpl
+import io.github.sds100.keymapper.home.HomeViewModel
+import io.github.sds100.keymapper.keymaps.ConfigKeyMapScreen
+import io.github.sds100.keymapper.keymaps.ConfigKeyMapViewModel
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class MainFragment : Fragment() {
+
+ @Inject
+ lateinit var navigationProvider: NavigationProviderImpl
+
+ @Inject
+ lateinit var dialogProvider: DialogProviderImpl
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ navigationProvider.setupFragmentNavigation(this)
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?,
+ ): View {
+ FragmentComposeBinding.inflate(inflater, container, false).apply {
+ composeView.apply {
+ // Dispose of the Composition when the view's LifecycleOwner
+ // is destroyed
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+ setContent {
+ val navController = rememberNavController()
+ SetupNavigation(navigationProvider, navController)
+
+ KeyMapperTheme {
+ BaseMainNavHost(
+ modifier = Modifier
+ .windowInsetsPadding(
+ WindowInsets.systemBars.only(sides = WindowInsetsSides.Horizontal)
+ .add(WindowInsets.displayCutout.only(sides = WindowInsetsSides.Horizontal)),
+ ),
+ navController = navController,
+ composableDestinations = {
+ composableDestinations()
+ },
+ )
+ }
+ }
+ }
+ return this.root
+ }
+ }
+
+ private fun NavGraphBuilder.composableDestinations() {
+ composable {
+ val snackbarState = remember { SnackbarHostState() }
+ val viewModel: HomeViewModel = hiltViewModel()
+
+ HomeKeyMapListScreen(
+ modifier = Modifier.fillMaxSize(),
+ viewModel = viewModel.keyMapListViewModel,
+ snackbarState = snackbarState,
+ onSettingsClick = viewModel::launchSettings,
+ onAboutClick = viewModel::launchAbout,
+ finishActivity = {
+ requireActivity().finish()
+ },
+ fabBottomPadding = 0.dp,
+ )
+ }
+
+ composable { backStackEntry ->
+ val viewModel: ConfigKeyMapViewModel = hiltViewModel()
+
+ backStackEntry.handleRouteArgs { args ->
+ viewModel.loadNewKeyMap(groupUid = args.groupUid)
+
+ if (args.showAdvancedTriggers) {
+ viewModel.configTriggerViewModel.showAdvancedTriggersBottomSheet = true
+ }
+ }
+
+ ConfigKeyMapScreen(
+ modifier = Modifier.fillMaxSize(),
+ viewModel = viewModel,
+ )
+ }
+
+ composable { backStackEntry ->
+ val viewModel: ConfigKeyMapViewModel = hiltViewModel()
+
+ backStackEntry.handleRouteArgs { args ->
+ viewModel.loadKeyMap(uid = args.keyMapUid)
+
+ if (args.showAdvancedTriggers) {
+ viewModel.configTriggerViewModel.showAdvancedTriggersBottomSheet = true
+ }
+ }
+
+ ConfigKeyMapScreen(
+ modifier = Modifier.fillMaxSize(),
+ viewModel = viewModel,
+ )
+ }
+
+ composable {
+ val viewModel: ChooseActionViewModel = hiltViewModel()
+
+ ChooseActionScreen(
+ modifier = Modifier.fillMaxSize(),
+ viewModel = viewModel,
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/sds100/keymapper/ServiceLocator.kt b/app/src/main/java/io/github/sds100/keymapper/ServiceLocator.kt
deleted file mode 100755
index 030d056d9a..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/ServiceLocator.kt
+++ /dev/null
@@ -1,324 +0,0 @@
-package io.github.sds100.keymapper
-
-import android.content.Context
-import androidx.datastore.preferences.preferencesDataStore
-import androidx.room.Room
-import io.github.sds100.keymapper.actions.sound.SoundsManager
-import io.github.sds100.keymapper.actions.sound.SoundsManagerImpl
-import io.github.sds100.keymapper.backup.BackupManager
-import io.github.sds100.keymapper.backup.BackupManagerImpl
-import io.github.sds100.keymapper.data.db.AppDatabase
-import io.github.sds100.keymapper.data.repositories.AccessibilityNodeRepository
-import io.github.sds100.keymapper.data.repositories.AccessibilityNodeRepositoryImpl
-import io.github.sds100.keymapper.data.repositories.FloatingButtonRepository
-import io.github.sds100.keymapper.data.repositories.FloatingLayoutRepository
-import io.github.sds100.keymapper.data.repositories.GroupRepository
-import io.github.sds100.keymapper.data.repositories.PreferenceRepository
-import io.github.sds100.keymapper.data.repositories.RoomFloatingButtonRepository
-import io.github.sds100.keymapper.data.repositories.RoomFloatingLayoutRepository
-import io.github.sds100.keymapper.data.repositories.RoomGroupRepository
-import io.github.sds100.keymapper.data.repositories.RoomKeyMapRepository
-import io.github.sds100.keymapper.data.repositories.RoomLogRepository
-import io.github.sds100.keymapper.data.repositories.SettingsPreferenceRepository
-import io.github.sds100.keymapper.keymaps.ConfigKeyMapUseCaseController
-import io.github.sds100.keymapper.logging.LogRepository
-import io.github.sds100.keymapper.purchasing.PurchasingManagerImpl
-import io.github.sds100.keymapper.shizuku.ShizukuAdapter
-import io.github.sds100.keymapper.system.accessibility.AccessibilityServiceAdapter
-import io.github.sds100.keymapper.system.airplanemode.AirplaneModeAdapter
-import io.github.sds100.keymapper.system.apps.AppShortcutAdapter
-import io.github.sds100.keymapper.system.apps.PackageManagerAdapter
-import io.github.sds100.keymapper.system.bluetooth.BluetoothAdapter
-import io.github.sds100.keymapper.system.camera.CameraAdapter
-import io.github.sds100.keymapper.system.clipboard.ClipboardAdapter
-import io.github.sds100.keymapper.system.devices.DevicesAdapter
-import io.github.sds100.keymapper.system.display.AndroidDisplayAdapter
-import io.github.sds100.keymapper.system.files.FileAdapter
-import io.github.sds100.keymapper.system.inputmethod.InputMethodAdapter
-import io.github.sds100.keymapper.system.intents.IntentAdapter
-import io.github.sds100.keymapper.system.leanback.LeanbackAdapter
-import io.github.sds100.keymapper.system.lock.LockScreenAdapter
-import io.github.sds100.keymapper.system.media.AndroidMediaAdapter
-import io.github.sds100.keymapper.system.network.NetworkAdapter
-import io.github.sds100.keymapper.system.nfc.NfcAdapter
-import io.github.sds100.keymapper.system.notifications.AndroidNotificationAdapter
-import io.github.sds100.keymapper.system.notifications.NotificationController
-import io.github.sds100.keymapper.system.notifications.NotificationReceiverAdapter
-import io.github.sds100.keymapper.system.permissions.AndroidPermissionAdapter
-import io.github.sds100.keymapper.system.permissions.SystemFeatureAdapter
-import io.github.sds100.keymapper.system.phone.PhoneAdapter
-import io.github.sds100.keymapper.system.popup.PopupMessageAdapter
-import io.github.sds100.keymapper.system.power.PowerAdapter
-import io.github.sds100.keymapper.system.ringtones.RingtoneAdapter
-import io.github.sds100.keymapper.system.root.SuAdapter
-import io.github.sds100.keymapper.system.url.OpenUrlAdapter
-import io.github.sds100.keymapper.system.vibrator.VibratorAdapter
-import io.github.sds100.keymapper.system.volume.VolumeAdapter
-import io.github.sds100.keymapper.util.ui.ResourceProviderImpl
-import kotlinx.coroutines.CoroutineScope
-
-/**
- * Created by sds100 on 17/05/2020.
- */
-object ServiceLocator {
-
- private var database: AppDatabase? = null
-
- private fun database(context: Context): AppDatabase {
- synchronized(this) {
- return database ?: createDatabase(context.applicationContext).also {
- this.database = it
- }
- }
- }
-
- @Volatile
- private var roomKeymapRepository: RoomKeyMapRepository? = null
-
- fun roomKeyMapRepository(context: Context): RoomKeyMapRepository {
- synchronized(this) {
- return roomKeymapRepository ?: RoomKeyMapRepository(
- database(context).keyMapDao(),
- database(context).fingerprintMapDao(),
- (context.applicationContext as KeyMapperApp).appCoroutineScope,
- ).also {
- this.roomKeymapRepository = it
- }
- }
- }
-
- @Volatile
- private var settingsRepository: PreferenceRepository? = null
-
- fun settingsRepository(context: Context): PreferenceRepository {
- synchronized(this) {
- return settingsRepository ?: SettingsPreferenceRepository(
- context.applicationContext,
- (context.applicationContext as KeyMapperApp).appCoroutineScope,
- ).also {
- this.settingsRepository = it
- }
- }
- }
-
- @Volatile
- private var logRepository: LogRepository? = null
-
- fun logRepository(context: Context): LogRepository {
- synchronized(this) {
- return logRepository ?: RoomLogRepository(
- (context.applicationContext as KeyMapperApp).appCoroutineScope,
- database(context).logEntryDao(),
- ).also {
- this.logRepository = it
- }
- }
- }
-
- @Volatile
- private var floatingLayoutRepository: FloatingLayoutRepository? = null
-
- fun floatingLayoutRepository(context: Context): FloatingLayoutRepository {
- synchronized(this) {
- return floatingLayoutRepository ?: RoomFloatingLayoutRepository(
- database(context).floatingLayoutDao(),
- (context.applicationContext as KeyMapperApp).appCoroutineScope,
- ).also {
- this.floatingLayoutRepository = it
- }
- }
- }
-
- @Volatile
- private var floatingButtonRepository: FloatingButtonRepository? = null
-
- fun floatingButtonRepository(context: Context): FloatingButtonRepository {
- synchronized(this) {
- return floatingButtonRepository ?: RoomFloatingButtonRepository(
- database(context).floatingButtonDao(),
- (context.applicationContext as KeyMapperApp).appCoroutineScope,
- ).also {
- this.floatingButtonRepository = it
- }
- }
- }
-
- @Volatile
- private var groupRepository: GroupRepository? = null
-
- fun groupRepository(context: Context): GroupRepository {
- synchronized(this) {
- return groupRepository ?: RoomGroupRepository(
- database(context).groupDao(),
- (context.applicationContext as KeyMapperApp).appCoroutineScope,
- ).also {
- this.groupRepository = it
- }
- }
- }
-
- @Volatile
- private var backupManager: BackupManager? = null
-
- fun backupManager(context: Context): BackupManager {
- synchronized(this) {
- return backupManager ?: createBackupManager(context).also {
- this.backupManager = it
- }
- }
- }
-
- private fun createBackupManager(context: Context): BackupManager = backupManager ?: BackupManagerImpl(
- (context.applicationContext as KeyMapperApp).appCoroutineScope,
- fileAdapter(context),
- roomKeyMapRepository(context),
- settingsRepository(context),
- floatingLayoutRepository(context),
- floatingButtonRepository(context),
- groupRepository(context),
- soundsManager(context),
- )
-
- @Volatile
- private var soundsManager: SoundsManager? = null
-
- fun soundsManager(context: Context): SoundsManager {
- synchronized(this) {
- return soundsManager ?: SoundsManagerImpl(
- (context.applicationContext as KeyMapperApp).appCoroutineScope,
- fileAdapter(context),
- ).also {
- this.soundsManager = it
- }
- }
- }
-
- @Volatile
- private var configKeyMapsController: ConfigKeyMapUseCaseController? = null
-
- fun configKeyMapsController(ctx: Context): ConfigKeyMapUseCaseController {
- synchronized(this) {
- return configKeyMapsController
- ?: createConfigKeyMapsController(ctx).also {
- configKeyMapsController = it
- }
- }
- }
-
- private fun createConfigKeyMapsController(ctx: Context): ConfigKeyMapUseCaseController {
- return ConfigKeyMapUseCaseController(
- appCoroutineScope(ctx),
- roomKeyMapRepository(ctx),
- devicesAdapter(ctx),
- settingsRepository(ctx),
- floatingLayoutRepository(ctx),
- floatingButtonRepository(ctx),
- accessibilityServiceAdapter(ctx),
- )
- }
-
- @Volatile
- private var accessibilityNodeRepository: AccessibilityNodeRepository? = null
-
- fun accessibilityNodeRepository(context: Context): AccessibilityNodeRepository {
- synchronized(this) {
- return accessibilityNodeRepository ?: AccessibilityNodeRepositoryImpl(
- (context.applicationContext as KeyMapperApp).appCoroutineScope,
- database(context).accessibilityNodeDao(),
- ).also {
- this.accessibilityNodeRepository = it
- }
- }
- }
-
- fun fileAdapter(context: Context): FileAdapter = (context.applicationContext as KeyMapperApp).fileAdapter
-
- fun inputMethodAdapter(context: Context): InputMethodAdapter = (context.applicationContext as KeyMapperApp).inputMethodAdapter
-
- fun devicesAdapter(context: Context): DevicesAdapter = (context.applicationContext as KeyMapperApp).devicesAdapter
-
- fun bluetoothAdapter(context: Context): BluetoothAdapter = (context.applicationContext as KeyMapperApp).bluetoothMonitor
-
- fun notificationController(context: Context): NotificationController = (context.applicationContext as KeyMapperApp).notificationController
-
- fun resourceProvider(context: Context): ResourceProviderImpl = (context.applicationContext as KeyMapperApp).resourceProvider
-
- fun packageManagerAdapter(context: Context): PackageManagerAdapter = (context.applicationContext as KeyMapperApp).packageManagerAdapter
-
- fun cameraAdapter(context: Context): CameraAdapter = (context.applicationContext as KeyMapperApp).cameraAdapter
-
- fun permissionAdapter(context: Context): AndroidPermissionAdapter = (context.applicationContext as KeyMapperApp).permissionAdapter
-
- fun systemFeatureAdapter(context: Context): SystemFeatureAdapter = (context.applicationContext as KeyMapperApp).systemFeatureAdapter
-
- fun accessibilityServiceAdapter(context: Context): AccessibilityServiceAdapter = (context.applicationContext as KeyMapperApp).accessibilityServiceAdapter
-
- fun notificationReceiverAdapter(context: Context): NotificationReceiverAdapter = (context.applicationContext as KeyMapperApp).notificationReceiverAdapter
-
- fun appShortcutAdapter(context: Context): AppShortcutAdapter = (context.applicationContext as KeyMapperApp).appShortcutAdapter
-
- fun notificationAdapter(context: Context): AndroidNotificationAdapter = (context.applicationContext as KeyMapperApp).notificationAdapter
-
- fun popupMessageAdapter(context: Context): PopupMessageAdapter = (context.applicationContext as KeyMapperApp).popupMessageAdapter
-
- fun vibratorAdapter(context: Context): VibratorAdapter = (context.applicationContext as KeyMapperApp).vibratorAdapter
-
- fun displayAdapter(context: Context): AndroidDisplayAdapter = (context.applicationContext as KeyMapperApp).displayAdapter
-
- fun audioAdapter(context: Context): VolumeAdapter = (context.applicationContext as KeyMapperApp).audioAdapter
-
- fun suAdapter(context: Context): SuAdapter = (context.applicationContext as KeyMapperApp).suAdapter
-
- fun intentAdapter(context: Context): IntentAdapter = (context.applicationContext as KeyMapperApp).intentAdapter
-
- fun phoneAdapter(context: Context): PhoneAdapter = (context.applicationContext as KeyMapperApp).phoneAdapter
-
- fun mediaAdapter(context: Context): AndroidMediaAdapter = (context.applicationContext as KeyMapperApp).mediaAdapter
-
- fun lockScreenAdapter(context: Context): LockScreenAdapter = (context.applicationContext as KeyMapperApp).lockScreenAdapter
-
- fun airplaneModeAdapter(context: Context): AirplaneModeAdapter = (context.applicationContext as KeyMapperApp).airplaneModeAdapter
-
- fun networkAdapter(context: Context): NetworkAdapter = (context.applicationContext as KeyMapperApp).networkAdapter
-
- fun nfcAdapter(context: Context): NfcAdapter = (context.applicationContext as KeyMapperApp).nfcAdapter
-
- fun openUrlAdapter(context: Context): OpenUrlAdapter = (context.applicationContext as KeyMapperApp).openUrlAdapter
-
- fun clipboardAdapter(context: Context): ClipboardAdapter = (context.applicationContext as KeyMapperApp).clipboardAdapter
-
- fun shizukuAdapter(context: Context): ShizukuAdapter = (context.applicationContext as KeyMapperApp).shizukuAdapter
-
- fun leanbackAdapter(context: Context): LeanbackAdapter = (context.applicationContext as KeyMapperApp).leanbackAdapter
-
- fun powerAdapter(context: Context): PowerAdapter = (context.applicationContext as KeyMapperApp).powerAdapter
-
- fun appCoroutineScope(context: Context): CoroutineScope = (context.applicationContext as KeyMapperApp).appCoroutineScope
-
- fun purchasingManager(context: Context): PurchasingManagerImpl = (context.applicationContext as KeyMapperApp).purchasingManager
-
- fun ringtoneAdapter(context: Context): RingtoneAdapter = (context.applicationContext as KeyMapperApp).ringtoneManagerAdapter
-
- private fun createDatabase(context: Context): AppDatabase = Room.databaseBuilder(
- context.applicationContext,
- AppDatabase::class.java,
- AppDatabase.DATABASE_NAME,
- ).addMigrations(
- AppDatabase.MIGRATION_1_2,
- AppDatabase.MIGRATION_2_3,
- AppDatabase.MIGRATION_3_4,
- AppDatabase.MIGRATION_4_5,
- AppDatabase.MIGRATION_5_6,
- AppDatabase.MIGRATION_6_7,
- AppDatabase.MIGRATION_7_8,
- AppDatabase.MIGRATION_8_9,
- AppDatabase.MIGRATION_9_10,
- AppDatabase.MIGRATION_10_11,
- AppDatabase.RoomMigration11To12(context.applicationContext.legacyFingerprintMapDataStore),
- AppDatabase.MIGRATION_12_13,
- AppDatabase.MIGRATION_13_14,
- AppDatabase.MIGRATION_17_18,
- ).build()
-
- private val Context.legacyFingerprintMapDataStore by preferencesDataStore("fingerprint_gestures")
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/UseCases.kt b/app/src/main/java/io/github/sds100/keymapper/UseCases.kt
deleted file mode 100644
index d57d0bf37b..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/UseCases.kt
+++ /dev/null
@@ -1,222 +0,0 @@
-package io.github.sds100.keymapper
-
-import android.content.Context
-import io.github.sds100.keymapper.actions.CreateActionUseCaseImpl
-import io.github.sds100.keymapper.actions.GetActionErrorUseCaseImpl
-import io.github.sds100.keymapper.actions.PerformActionsUseCaseImpl
-import io.github.sds100.keymapper.api.KeyEventRelayServiceWrapper
-import io.github.sds100.keymapper.constraints.DetectConstraintsUseCaseImpl
-import io.github.sds100.keymapper.constraints.GetConstraintErrorUseCaseImpl
-import io.github.sds100.keymapper.floating.ListFloatingLayoutsUseCase
-import io.github.sds100.keymapper.floating.ListFloatingLayoutsUseCaseImpl
-import io.github.sds100.keymapper.keymaps.ConfigKeyMapUseCase
-import io.github.sds100.keymapper.keymaps.CreateKeyMapShortcutUseCaseImpl
-import io.github.sds100.keymapper.keymaps.DisplayKeyMapUseCase
-import io.github.sds100.keymapper.keymaps.DisplayKeyMapUseCaseImpl
-import io.github.sds100.keymapper.keymaps.FingerprintGesturesSupportedUseCaseImpl
-import io.github.sds100.keymapper.keymaps.PauseKeyMapsUseCaseImpl
-import io.github.sds100.keymapper.keymaps.detection.DetectKeyMapsUseCaseImpl
-import io.github.sds100.keymapper.onboarding.OnboardingUseCaseImpl
-import io.github.sds100.keymapper.reroutekeyevents.RerouteKeyEventsUseCaseImpl
-import io.github.sds100.keymapper.shizuku.ShizukuInputEventInjector
-import io.github.sds100.keymapper.sorting.SortKeyMapsUseCase
-import io.github.sds100.keymapper.sorting.SortKeyMapsUseCaseImpl
-import io.github.sds100.keymapper.system.Shell
-import io.github.sds100.keymapper.system.accessibility.ControlAccessibilityServiceUseCase
-import io.github.sds100.keymapper.system.accessibility.ControlAccessibilityServiceUseCaseImpl
-import io.github.sds100.keymapper.system.accessibility.IAccessibilityService
-import io.github.sds100.keymapper.system.accessibility.MyAccessibilityService
-import io.github.sds100.keymapper.system.apps.DisplayAppsUseCase
-import io.github.sds100.keymapper.system.apps.DisplayAppsUseCaseImpl
-import io.github.sds100.keymapper.system.inputmethod.ImeInputEventInjectorImpl
-import io.github.sds100.keymapper.system.inputmethod.ShowInputMethodPickerUseCase
-import io.github.sds100.keymapper.system.inputmethod.ShowInputMethodPickerUseCaseImpl
-import io.github.sds100.keymapper.system.inputmethod.ToggleCompatibleImeUseCaseImpl
-
-/**
- * Created by sds100 on 03/03/2021.
- */
-object UseCases {
-
- fun listFloatingLayouts(ctx: Context): ListFloatingLayoutsUseCase = ListFloatingLayoutsUseCaseImpl(
- ServiceLocator.floatingLayoutRepository(ctx),
- ServiceLocator.purchasingManager(ctx),
- ServiceLocator.accessibilityServiceAdapter(ctx),
- ServiceLocator.settingsRepository(ctx),
- )
-
- fun displayPackages(ctx: Context): DisplayAppsUseCase = DisplayAppsUseCaseImpl(
- ServiceLocator.packageManagerAdapter(ctx),
- )
-
- fun displayKeyMap(ctx: Context): DisplayKeyMapUseCase = DisplayKeyMapUseCaseImpl(
- ServiceLocator.permissionAdapter(ctx),
- ServiceLocator.inputMethodAdapter(ctx),
- ServiceLocator.packageManagerAdapter(ctx),
- ServiceLocator.settingsRepository(ctx),
- ServiceLocator.accessibilityServiceAdapter(ctx),
- ServiceLocator.settingsRepository(ctx),
- ServiceLocator.purchasingManager(ctx),
- ServiceLocator.ringtoneAdapter(ctx),
- getActionError(ctx),
- getConstraintError(ctx),
- )
-
- fun configKeyMap(ctx: Context): ConfigKeyMapUseCase = ServiceLocator.configKeyMapsController(ctx)
-
- fun getActionError(ctx: Context) = GetActionErrorUseCaseImpl(
- ServiceLocator.packageManagerAdapter(ctx),
- ServiceLocator.inputMethodAdapter(ctx),
- ServiceLocator.permissionAdapter(ctx),
- ServiceLocator.systemFeatureAdapter(ctx),
- ServiceLocator.cameraAdapter(ctx),
- ServiceLocator.soundsManager(ctx),
- ServiceLocator.shizukuAdapter(ctx),
- ServiceLocator.ringtoneAdapter(ctx),
- )
-
- fun getConstraintError(ctx: Context) = GetConstraintErrorUseCaseImpl(
- ServiceLocator.packageManagerAdapter(ctx),
- ServiceLocator.permissionAdapter(ctx),
- ServiceLocator.systemFeatureAdapter(ctx),
- ServiceLocator.inputMethodAdapter(ctx),
- ServiceLocator.cameraAdapter(ctx),
- )
-
- fun onboarding(ctx: Context) = OnboardingUseCaseImpl(
- ServiceLocator.settingsRepository(ctx),
- ServiceLocator.fileAdapter(ctx),
- ServiceLocator.leanbackAdapter(ctx),
- ServiceLocator.shizukuAdapter(ctx),
- ServiceLocator.permissionAdapter(ctx),
- ServiceLocator.packageManagerAdapter(ctx),
- ServiceLocator.purchasingManager(ctx),
- ServiceLocator.roomKeyMapRepository(ctx),
- )
-
- fun createKeymapShortcut(ctx: Context) = CreateKeyMapShortcutUseCaseImpl(
- ServiceLocator.appShortcutAdapter(ctx),
- ServiceLocator.resourceProvider(ctx),
- )
-
- fun fingerprintGesturesSupported(ctx: Context) = FingerprintGesturesSupportedUseCaseImpl(ServiceLocator.settingsRepository(ctx))
-
- fun pauseKeyMaps(ctx: Context) = PauseKeyMapsUseCaseImpl(
- ServiceLocator.settingsRepository(ctx),
- ServiceLocator.mediaAdapter(ctx),
- ServiceLocator.ringtoneAdapter(ctx),
- )
-
- fun showImePicker(ctx: Context): ShowInputMethodPickerUseCase = ShowInputMethodPickerUseCaseImpl(
- ServiceLocator.inputMethodAdapter(ctx),
- )
-
- fun controlAccessibilityService(ctx: Context): ControlAccessibilityServiceUseCase = ControlAccessibilityServiceUseCaseImpl(
- ServiceLocator.accessibilityServiceAdapter(ctx),
- ServiceLocator.permissionAdapter(ctx),
- )
-
- fun toggleCompatibleIme(ctx: Context) = ToggleCompatibleImeUseCaseImpl(
- ServiceLocator.inputMethodAdapter(ctx),
- )
-
- fun detectConstraints(service: MyAccessibilityService) = DetectConstraintsUseCaseImpl(
- service,
- ServiceLocator.mediaAdapter(service),
- ServiceLocator.devicesAdapter(service),
- ServiceLocator.displayAdapter(service),
- ServiceLocator.cameraAdapter(service),
- ServiceLocator.networkAdapter(service),
- ServiceLocator.inputMethodAdapter(service),
- ServiceLocator.lockScreenAdapter(service),
- ServiceLocator.phoneAdapter(service),
- ServiceLocator.powerAdapter(service),
- )
-
- fun performActions(
- ctx: Context,
- service: IAccessibilityService,
- keyEventRelayService: KeyEventRelayServiceWrapper,
- ) = PerformActionsUseCaseImpl(
- (ctx.applicationContext as KeyMapperApp).appCoroutineScope,
- service,
- ServiceLocator.inputMethodAdapter(ctx),
- ServiceLocator.fileAdapter(ctx),
- ServiceLocator.suAdapter(ctx),
- Shell,
- ServiceLocator.intentAdapter(ctx),
- getActionError(ctx),
- keyMapperImeMessenger(ctx, keyEventRelayService),
- ShizukuInputEventInjector(),
- ServiceLocator.packageManagerAdapter(ctx),
- ServiceLocator.appShortcutAdapter(ctx),
- ServiceLocator.popupMessageAdapter(ctx),
- ServiceLocator.devicesAdapter(ctx),
- ServiceLocator.phoneAdapter(ctx),
- ServiceLocator.audioAdapter(ctx),
- ServiceLocator.cameraAdapter(ctx),
- ServiceLocator.displayAdapter(ctx),
- ServiceLocator.lockScreenAdapter(ctx),
- ServiceLocator.mediaAdapter(ctx),
- ServiceLocator.airplaneModeAdapter(ctx),
- ServiceLocator.networkAdapter(ctx),
- ServiceLocator.bluetoothAdapter(ctx),
- ServiceLocator.nfcAdapter(ctx),
- ServiceLocator.openUrlAdapter(ctx),
- ServiceLocator.resourceProvider(ctx),
- ServiceLocator.settingsRepository(ctx),
- ServiceLocator.soundsManager(ctx),
- ServiceLocator.permissionAdapter(ctx),
- ServiceLocator.notificationReceiverAdapter(ctx),
- ServiceLocator.ringtoneAdapter(ctx),
- )
-
- fun detectKeyMaps(
- ctx: Context,
- service: IAccessibilityService,
- keyEventRelayService: KeyEventRelayServiceWrapper,
- ) = DetectKeyMapsUseCaseImpl(
- ServiceLocator.roomKeyMapRepository(ctx),
- ServiceLocator.floatingButtonRepository(ctx),
- ServiceLocator.groupRepository(ctx),
- ServiceLocator.settingsRepository(ctx),
- ServiceLocator.suAdapter(ctx),
- ServiceLocator.displayAdapter(ctx),
- ServiceLocator.audioAdapter(ctx),
- keyMapperImeMessenger(ctx, keyEventRelayService),
- service,
- ShizukuInputEventInjector(),
- ServiceLocator.popupMessageAdapter(ctx),
- ServiceLocator.permissionAdapter(ctx),
- ServiceLocator.resourceProvider(ctx),
- ServiceLocator.vibratorAdapter(ctx),
- ServiceLocator.appCoroutineScope(ctx),
- )
-
- fun rerouteKeyEvents(ctx: Context, keyEventRelayService: KeyEventRelayServiceWrapper) = RerouteKeyEventsUseCaseImpl(
- ServiceLocator.inputMethodAdapter(ctx),
- keyMapperImeMessenger(ctx, keyEventRelayService),
- ServiceLocator.settingsRepository(ctx),
- )
-
- fun createAction(ctx: Context) = CreateActionUseCaseImpl(
- ServiceLocator.inputMethodAdapter(ctx),
- ServiceLocator.systemFeatureAdapter(ctx),
- ServiceLocator.cameraAdapter(ctx),
- ServiceLocator.permissionAdapter(ctx),
- )
-
- private fun keyMapperImeMessenger(
- ctx: Context,
- keyEventRelayService: KeyEventRelayServiceWrapper,
- ) = ImeInputEventInjectorImpl(
- ctx,
- keyEventRelayService,
- ServiceLocator.inputMethodAdapter(ctx),
- )
-
- fun sortKeyMapsUseCase(ctx: Context): SortKeyMapsUseCase = SortKeyMapsUseCaseImpl(
- ServiceLocator.settingsRepository(ctx),
- displayKeyMap(ctx),
- )
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/ChooseActionFragment.kt b/app/src/main/java/io/github/sds100/keymapper/actions/ChooseActionFragment.kt
deleted file mode 100644
index 8bc9c8f483..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/actions/ChooseActionFragment.kt
+++ /dev/null
@@ -1,103 +0,0 @@
-package io.github.sds100.keymapper.actions
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.WindowInsetsSides
-import androidx.compose.foundation.layout.add
-import androidx.compose.foundation.layout.displayCutout
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.only
-import androidx.compose.foundation.layout.systemBars
-import androidx.compose.foundation.layout.windowInsetsPadding
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.ViewCompositionStrategy
-import androidx.core.os.bundleOf
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.setFragmentResult
-import androidx.fragment.app.viewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.withStateAtLeast
-import androidx.navigation.findNavController
-import androidx.navigation.fragment.findNavController
-import androidx.navigation.fragment.navArgs
-import io.github.sds100.keymapper.compose.KeyMapperTheme
-import io.github.sds100.keymapper.databinding.FragmentComposeBinding
-import io.github.sds100.keymapper.util.Inject
-import io.github.sds100.keymapper.util.launchRepeatOnLifecycle
-import io.github.sds100.keymapper.util.ui.setupNavigation
-import io.github.sds100.keymapper.util.ui.showPopups
-import io.github.sds100.keymapper.util.viewLifecycleScope
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
-import kotlinx.serialization.json.Json
-
-class ChooseActionFragment : Fragment() {
-
- companion object {
- const val EXTRA_ACTION = "extra_action"
- }
-
- private val args: ChooseActionFragmentArgs by navArgs()
-
- private val viewModel by viewModels {
- Inject.chooseActionViewModel(requireContext())
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- viewModel.setupNavigation(this)
-
- launchRepeatOnLifecycle(Lifecycle.State.CREATED) {
- viewModel.returnAction.collectLatest { action ->
- viewLifecycleScope.launch {
- withStateAtLeast(Lifecycle.State.RESUMED) {
- setFragmentResult(
- args.requestKey,
- bundleOf(EXTRA_ACTION to Json.encodeToString(action)),
- )
- findNavController().navigateUp()
- }
- }
- }
- }
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?,
- ): View {
- FragmentComposeBinding.inflate(inflater, container, false).apply {
- composeView.apply {
- // Dispose of the Composition when the view's LifecycleOwner
- // is destroyed
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- setContent {
- KeyMapperTheme {
- ChooseActionScreen(
- modifier = Modifier
- .fillMaxSize()
- .windowInsetsPadding(
- WindowInsets.systemBars.only(sides = WindowInsetsSides.Horizontal)
- .add(WindowInsets.displayCutout.only(sides = WindowInsetsSides.Horizontal)),
- ),
- viewModel = viewModel,
- onNavigateBack = findNavController()::navigateUp,
- )
- }
- }
- }
- return this.root
- }
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- viewModel.showPopups(this, view)
- }
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/DisplayActionUseCase.kt b/app/src/main/java/io/github/sds100/keymapper/actions/DisplayActionUseCase.kt
deleted file mode 100644
index 4166825086..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/actions/DisplayActionUseCase.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package io.github.sds100.keymapper.actions
-
-import android.graphics.drawable.Drawable
-import io.github.sds100.keymapper.util.Error
-import io.github.sds100.keymapper.util.Result
-import kotlinx.coroutines.flow.Flow
-
-interface DisplayActionUseCase : GetActionErrorUseCase {
- val showDeviceDescriptors: Flow
- fun getAppName(packageName: String): Result
- fun getAppIcon(packageName: String): Result
- fun getInputMethodLabel(imeId: String): Result
- fun getRingtoneLabel(uri: String): Result
- suspend fun fixError(error: Error)
- fun neverShowDndTriggerError()
- fun startAccessibilityService(): Boolean
- fun restartAccessibilityService(): Boolean
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/TestActionUseCase.kt b/app/src/main/java/io/github/sds100/keymapper/actions/TestActionUseCase.kt
deleted file mode 100644
index 6d4dd00ca1..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/actions/TestActionUseCase.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package io.github.sds100.keymapper.actions
-
-import io.github.sds100.keymapper.system.accessibility.ServiceAdapter
-import io.github.sds100.keymapper.util.Result
-import io.github.sds100.keymapper.util.ServiceEvent
-
-/**
- * Created by sds100 on 20/02/2021.
- */
-
-class TestActionUseCaseImpl(
- private val serviceAdapter: ServiceAdapter,
-) : TestActionUseCase {
- override suspend fun invoke(action: ActionData): Result<*> =
- serviceAdapter.send(ServiceEvent.TestAction(action))
-}
-
-interface TestActionUseCase {
- suspend operator fun invoke(action: ActionData): Result<*>
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/sound/ChooseSoundFileUseCase.kt b/app/src/main/java/io/github/sds100/keymapper/actions/sound/ChooseSoundFileUseCase.kt
deleted file mode 100644
index 2e365bf23b..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/actions/sound/ChooseSoundFileUseCase.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-package io.github.sds100.keymapper.actions.sound
-
-import io.github.sds100.keymapper.system.files.FileAdapter
-import io.github.sds100.keymapper.util.Error
-import io.github.sds100.keymapper.util.Result
-import io.github.sds100.keymapper.util.Success
-import kotlinx.coroutines.flow.StateFlow
-
-/**
- * Created by sds100 on 25/06/2021.
- */
-
-class ChooseSoundFileUseCaseImpl(
- private val fileAdapter: FileAdapter,
- private val soundsManager: SoundsManager,
-) : ChooseSoundFileUseCase {
- override val soundFiles = soundsManager.soundFiles
-
- override suspend fun saveSound(uri: String): Result = soundsManager.saveNewSound(uri)
-
- override fun getSoundFileName(uri: String): Result {
- val name = fileAdapter.getFileFromUri(uri).name
-
- return if (name == null) {
- Error.NoFileName
- } else {
- Success(name)
- }
- }
-}
-
-interface ChooseSoundFileUseCase {
-
- /**
- * @return the sound file uid
- */
- suspend fun saveSound(uri: String): Result
- val soundFiles: StateFlow>
- fun getSoundFileName(uri: String): Result
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/sound/SoundFileInfo.kt b/app/src/main/java/io/github/sds100/keymapper/actions/sound/SoundFileInfo.kt
deleted file mode 100644
index 5178df7542..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/actions/sound/SoundFileInfo.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package io.github.sds100.keymapper.actions.sound
-
-/**
- * Created by sds100 on 25/06/2021.
- */
-data class SoundFileInfo(val uid: String, val name: String)
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/uielement/InteractUiElementFragment.kt b/app/src/main/java/io/github/sds100/keymapper/actions/uielement/InteractUiElementFragment.kt
deleted file mode 100644
index c8f0f24c10..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/actions/uielement/InteractUiElementFragment.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-package io.github.sds100.keymapper.actions.uielement
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.WindowInsetsSides
-import androidx.compose.foundation.layout.add
-import androidx.compose.foundation.layout.displayCutout
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.only
-import androidx.compose.foundation.layout.systemBars
-import androidx.compose.foundation.layout.windowInsetsPadding
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.ViewCompositionStrategy
-import androidx.core.os.bundleOf
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.setFragmentResult
-import androidx.fragment.app.viewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.withStateAtLeast
-import androidx.navigation.findNavController
-import androidx.navigation.fragment.findNavController
-import androidx.navigation.fragment.navArgs
-import io.github.sds100.keymapper.compose.KeyMapperTheme
-import io.github.sds100.keymapper.databinding.FragmentComposeBinding
-import io.github.sds100.keymapper.util.Inject
-import io.github.sds100.keymapper.util.launchRepeatOnLifecycle
-import io.github.sds100.keymapper.util.ui.showPopups
-import io.github.sds100.keymapper.util.viewLifecycleScope
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
-import kotlinx.serialization.json.Json
-
-class InteractUiElementFragment : Fragment() {
-
- companion object {
- const val EXTRA_ACTION = "extra_action"
- }
-
- private val args: InteractUiElementFragmentArgs by navArgs()
-
- private val viewModel by viewModels {
- Inject.interactUiElementViewModel(requireContext())
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- args.action?.let { argsAction -> viewModel.loadAction(Json.decodeFromString(argsAction)) }
-
- launchRepeatOnLifecycle(Lifecycle.State.CREATED) {
- viewModel.returnAction.collectLatest { action ->
- viewLifecycleScope.launch {
- withStateAtLeast(Lifecycle.State.RESUMED) {
- setFragmentResult(
- args.requestKey,
- bundleOf(EXTRA_ACTION to Json.encodeToString(action)),
- )
- findNavController().navigateUp()
- }
- }
- }
- }
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?,
- ): View {
- FragmentComposeBinding.inflate(inflater, container, false).apply {
- composeView.apply {
- // Dispose of the Composition when the view's LifecycleOwner
- // is destroyed
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- setContent {
- KeyMapperTheme {
- InteractUiElementScreen(
- modifier = Modifier
- .fillMaxSize()
- .windowInsetsPadding(
- WindowInsets.systemBars.only(sides = WindowInsetsSides.Horizontal)
- .add(WindowInsets.displayCutout.only(sides = WindowInsetsSides.Horizontal)),
- ),
- viewModel = viewModel,
- navigateBack = findNavController()::navigateUp,
- )
- }
- }
- }
- return this.root
- }
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- viewModel.showPopups(this, view)
- }
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/api/LaunchKeyMapShortcutActivity.kt b/app/src/main/java/io/github/sds100/keymapper/api/LaunchKeyMapShortcutActivity.kt
deleted file mode 100644
index b83fec2085..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/api/LaunchKeyMapShortcutActivity.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package io.github.sds100.keymapper.api
-
-import android.app.Activity
-import android.content.Intent
-import android.os.Bundle
-import io.github.sds100.keymapper.Constants
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.ServiceLocator
-import io.github.sds100.keymapper.system.accessibility.ServiceState
-import splitties.toast.toast
-
-/**
- * Created by sds100 on 08/09/20.
- */
-
-// DON'T MOVE THIS CLASS TO A DIFFERENT PACKAGE BECAUSE IT BREAKS THE API
-/**
- * Use basic Activity, NOT AppCompatActivity so the NoDisplay theme works. Otherwise an
- * exception may be thrown because the theme doesn't extend AppCompat.
- */
-class LaunchKeyMapShortcutActivity : Activity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- val accessibilityServiceState = ServiceLocator.accessibilityServiceAdapter(this).state.value
-
- when (accessibilityServiceState) {
- ServiceState.ENABLED ->
- if (intent.action == Api.ACTION_TRIGGER_KEYMAP_BY_UID) {
- Intent(Api.ACTION_TRIGGER_KEYMAP_BY_UID).apply {
- setPackage(Constants.PACKAGE_NAME)
-
- val uuid = intent.getStringExtra(Api.EXTRA_KEYMAP_UID)
- putExtra(Api.EXTRA_KEYMAP_UID, uuid)
-
- sendBroadcast(this)
- }
- }
-
- ServiceState.CRASHED -> toast(R.string.error_accessibility_service_crashed)
- ServiceState.DISABLED -> toast(R.string.error_accessibility_service_disabled)
- }
-
- finish()
- }
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/constraints/ChooseConstraintFragment.kt b/app/src/main/java/io/github/sds100/keymapper/constraints/ChooseConstraintFragment.kt
deleted file mode 100644
index 727bd91534..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/constraints/ChooseConstraintFragment.kt
+++ /dev/null
@@ -1,103 +0,0 @@
-package io.github.sds100.keymapper.constraints
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.WindowInsetsSides
-import androidx.compose.foundation.layout.add
-import androidx.compose.foundation.layout.displayCutout
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.only
-import androidx.compose.foundation.layout.systemBars
-import androidx.compose.foundation.layout.windowInsetsPadding
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.ViewCompositionStrategy
-import androidx.core.os.bundleOf
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.setFragmentResult
-import androidx.fragment.app.viewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.withStateAtLeast
-import androidx.navigation.findNavController
-import androidx.navigation.fragment.findNavController
-import androidx.navigation.fragment.navArgs
-import io.github.sds100.keymapper.compose.KeyMapperTheme
-import io.github.sds100.keymapper.databinding.FragmentComposeBinding
-import io.github.sds100.keymapper.util.Inject
-import io.github.sds100.keymapper.util.launchRepeatOnLifecycle
-import io.github.sds100.keymapper.util.ui.setupNavigation
-import io.github.sds100.keymapper.util.ui.showPopups
-import io.github.sds100.keymapper.util.viewLifecycleScope
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
-import kotlinx.serialization.json.Json
-
-class ChooseConstraintFragment : Fragment() {
-
- companion object {
- const val EXTRA_CONSTRAINT = "extra_constraint"
- }
-
- private val navArgs by navArgs()
-
- private val viewModel: ChooseConstraintViewModel by viewModels {
- Inject.chooseConstraintListViewModel(requireContext())
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- viewModel.setupNavigation(this)
-
- launchRepeatOnLifecycle(Lifecycle.State.CREATED) {
- viewModel.returnResult.collectLatest { constraint ->
- viewLifecycleScope.launch {
- withStateAtLeast(Lifecycle.State.RESUMED) {
- setFragmentResult(
- navArgs.requestKey,
- bundleOf(EXTRA_CONSTRAINT to Json.encodeToString(constraint)),
- )
- findNavController().navigateUp()
- }
- }
- }
- }
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?,
- ): View {
- FragmentComposeBinding.inflate(inflater, container, false).apply {
- composeView.apply {
- // Dispose of the Composition when the view's LifecycleOwner
- // is destroyed
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- setContent {
- KeyMapperTheme {
- ChooseConstraintScreen(
- modifier = Modifier
- .fillMaxSize()
- .windowInsetsPadding(
- WindowInsets.systemBars.only(sides = WindowInsetsSides.Horizontal)
- .add(WindowInsets.displayCutout.only(sides = WindowInsetsSides.Horizontal)),
- ),
- viewModel = viewModel,
- onNavigateBack = findNavController()::navigateUp,
- )
- }
- }
- }
- return this.root
- }
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- viewModel.showPopups(this, view)
- }
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/constraints/ConstraintMode.kt b/app/src/main/java/io/github/sds100/keymapper/constraints/ConstraintMode.kt
deleted file mode 100644
index ddc7a85d6c..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/constraints/ConstraintMode.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package io.github.sds100.keymapper.constraints
-
-/**
- * Created by sds100 on 03/03/2021.
- */
-enum class ConstraintMode {
- AND,
- OR,
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/constraints/DisplayConstraintUseCase.kt b/app/src/main/java/io/github/sds100/keymapper/constraints/DisplayConstraintUseCase.kt
deleted file mode 100644
index 6b5bec73ee..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/constraints/DisplayConstraintUseCase.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package io.github.sds100.keymapper.constraints
-
-import android.graphics.drawable.Drawable
-import io.github.sds100.keymapper.util.Error
-import io.github.sds100.keymapper.util.Result
-
-interface DisplayConstraintUseCase : GetConstraintErrorUseCase {
- fun getAppName(packageName: String): Result
- fun getAppIcon(packageName: String): Result
- fun getInputMethodLabel(imeId: String): Result
- fun neverShowDndTriggerError()
- suspend fun fixError(error: Error)
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/groups/GroupListItemModel.kt b/app/src/main/java/io/github/sds100/keymapper/groups/GroupListItemModel.kt
deleted file mode 100644
index 9f53a1da74..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/groups/GroupListItemModel.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package io.github.sds100.keymapper.groups
-
-import io.github.sds100.keymapper.util.ui.compose.ComposeIconInfo
-
-data class GroupListItemModel(val uid: String, val name: String, val icon: ComposeIconInfo? = null)
diff --git a/app/src/main/java/io/github/sds100/keymapper/home/HomeDestination.kt b/app/src/main/java/io/github/sds100/keymapper/home/HomeDestination.kt
deleted file mode 100644
index 33d985c64f..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/home/HomeDestination.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package io.github.sds100.keymapper.home
-
-sealed class HomeDestination(val route: String) {
- data object KeyMaps : HomeDestination("key_maps")
- data object FloatingButtons : HomeDestination("floating_buttons")
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/home/HomeFragment.kt b/app/src/main/java/io/github/sds100/keymapper/home/HomeFragment.kt
deleted file mode 100644
index 5ba4a8c508..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/home/HomeFragment.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-package io.github.sds100.keymapper.home
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.WindowInsetsSides
-import androidx.compose.foundation.layout.add
-import androidx.compose.foundation.layout.displayCutout
-import androidx.compose.foundation.layout.only
-import androidx.compose.foundation.layout.systemBars
-import androidx.compose.foundation.layout.windowInsetsPadding
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.ViewCompositionStrategy
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.activityViewModels
-import androidx.navigation.findNavController
-import io.github.sds100.keymapper.ActivityViewModel
-import io.github.sds100.keymapper.BaseMainActivity
-import io.github.sds100.keymapper.NavAppDirections
-import io.github.sds100.keymapper.ServiceLocator
-import io.github.sds100.keymapper.compose.KeyMapperTheme
-import io.github.sds100.keymapper.databinding.FragmentComposeBinding
-import io.github.sds100.keymapper.util.Inject
-import io.github.sds100.keymapper.util.ui.setupNavigation
-import io.github.sds100.keymapper.util.ui.showPopups
-
-class HomeFragment : Fragment() {
-
- private val homeViewModel: HomeViewModel by activityViewModels {
- Inject.homeViewModel(requireContext())
- }
-
- val activityViewModel: ActivityViewModel by activityViewModels {
- ActivityViewModel.Factory(ServiceLocator.resourceProvider(requireContext()))
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- homeViewModel.setupNavigation(this)
- homeViewModel.keyMapListViewModel.setupNavigation(this)
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?,
- ): View {
- val startDestination =
- if (!activityViewModel.handledActivityLaunchIntent &&
- requireActivity().intent?.action == BaseMainActivity.ACTION_USE_FLOATING_BUTTONS
- ) {
- activityViewModel.handledActivityLaunchIntent = true
- HomeDestination.FloatingButtons
- } else {
- HomeDestination.KeyMaps
- }
-
- FragmentComposeBinding.inflate(inflater, container, false).apply {
- composeView.apply {
- // Dispose of the Composition when the view's LifecycleOwner
- // is destroyed
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- setContent {
- KeyMapperTheme {
- HomeScreen(
- modifier = Modifier
- .windowInsetsPadding(
- WindowInsets.systemBars.only(sides = WindowInsetsSides.Horizontal)
- .add(WindowInsets.displayCutout.only(sides = WindowInsetsSides.Horizontal)),
- ),
- viewModel = homeViewModel,
- onSettingsClick = {
- findNavController().navigate(NavAppDirections.toSettingsFragment())
- },
- onAboutClick = {
- findNavController().navigate(NavAppDirections.actionGlobalAboutFragment())
- },
- finishActivity = requireActivity()::finish,
- startDestination = startDestination,
- )
- }
- }
- }
- return this.root
- }
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- homeViewModel.showPopups(this, view)
- homeViewModel.keyMapListViewModel.showPopups(this, view)
- homeViewModel.listFloatingLayoutsViewModel.showPopups(this, view)
- }
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/home/HomeScreen.kt b/app/src/main/java/io/github/sds100/keymapper/home/HomeScreen.kt
deleted file mode 100644
index b8ffce2f7a..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/home/HomeScreen.kt
+++ /dev/null
@@ -1,189 +0,0 @@
-package io.github.sds100.keymapper.home
-
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.EnterTransition
-import androidx.compose.animation.ExitTransition
-import androidx.compose.animation.slideInVertically
-import androidx.compose.animation.slideOutVertically
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Badge
-import androidx.compose.material3.BadgedBox
-import androidx.compose.material3.Icon
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.NavigationBar
-import androidx.compose.material3.NavigationBarItem
-import androidx.compose.material3.SnackbarHostState
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import androidx.navigation.NavDestination.Companion.hierarchy
-import androidx.navigation.NavGraph.Companion.findStartDestination
-import androidx.navigation.NavHostController
-import androidx.navigation.compose.NavHost
-import androidx.navigation.compose.composable
-import androidx.navigation.compose.currentBackStackEntryAsState
-import androidx.navigation.compose.rememberNavController
-import io.github.sds100.keymapper.util.ui.SelectionState
-
-@Composable
-fun HomeScreen(
- modifier: Modifier = Modifier,
- viewModel: HomeViewModel,
- onSettingsClick: () -> Unit,
- onAboutClick: () -> Unit,
- finishActivity: () -> Unit,
- startDestination: HomeDestination = HomeDestination.KeyMaps,
-) {
- val navController = rememberNavController()
- val navBarItems by viewModel.navBarItems.collectAsStateWithLifecycle()
-
- val snackbarState = remember { SnackbarHostState() }
- val selectionState by viewModel.keyMapListViewModel.multiSelectProvider.state.collectAsStateWithLifecycle()
-
- HomeScreen(
- modifier = modifier,
- isSelectingKeyMaps = selectionState is SelectionState.Selecting,
- startDestination = startDestination,
- navController = navController,
- navBarItems = navBarItems,
- keyMapsContent = {
- HomeKeyMapListScreen(
- viewModel = viewModel.keyMapListViewModel,
- snackbarState = snackbarState,
- onSettingsClick = onSettingsClick,
- onAboutClick = onAboutClick,
- finishActivity = finishActivity,
- fabBottomPadding = if (navBarItems.size == 1) {
- 0.dp
- } else {
- 80.dp
- },
- )
- },
- floatingButtonsContent = {
- HomeFloatingLayoutsScreen(
- viewModel = viewModel.listFloatingLayoutsViewModel,
- navController = navController,
- snackbarState = snackbarState,
- fabBottomPadding = if (navBarItems.size == 1) {
- 0.dp
- } else {
- 80.dp
- },
- )
- },
- )
-}
-
-@Composable
-private fun HomeScreen(
- modifier: Modifier = Modifier,
- isSelectingKeyMaps: Boolean,
- startDestination: HomeDestination = HomeDestination.KeyMaps,
- navController: NavHostController,
- navBarItems: List,
- keyMapsContent: @Composable () -> Unit,
- floatingButtonsContent: @Composable () -> Unit,
-) {
- val navBackStackEntry by navController.currentBackStackEntryAsState()
- val currentDestination = navBackStackEntry?.destination
-
- Column(modifier) {
- Box(contentAlignment = Alignment.BottomCenter) {
- NavHost(
- modifier = Modifier.fillMaxSize(),
- contentAlignment = Alignment.TopCenter,
- navController = navController,
- startDestination = startDestination.route,
- // use no animations because otherwise the transition freezes
- // when quickly navigating to another page while the transition is still happening.
- enterTransition = { EnterTransition.None },
- exitTransition = { ExitTransition.None },
- ) {
- composable(HomeDestination.KeyMaps.route) {
- keyMapsContent()
- }
- composable(HomeDestination.FloatingButtons.route) {
- floatingButtonsContent()
- }
- }
-
- this@Column.AnimatedVisibility(
- visible = !isSelectingKeyMaps && navBarItems.size > 1,
- enter = slideInVertically { it },
- exit = slideOutVertically { it },
- ) {
- NavigationBar {
- navBarItems.forEach { item ->
- NavigationBarItem(
- icon = {
- if (item.badge == null) {
- Icon(item.icon, contentDescription = null)
- } else {
- BadgedBox(
- badge = {
- Badge(
- modifier = Modifier
- .height(22.dp)
- .padding(start = 10.dp),
- containerColor = MaterialTheme.colorScheme.primary,
- contentColor = MaterialTheme.colorScheme.onPrimary,
- ) {
- Text(
- modifier = Modifier.padding(horizontal = 2.dp),
- text = item.badge,
- style = MaterialTheme.typography.labelLarge,
- )
- }
- },
- ) {
- Icon(item.icon, contentDescription = null)
- }
- }
- },
- label = {
- Text(
- item.label,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- )
- },
- selected = currentDestination?.hierarchy?.any { it.route == item.destination.route } == true,
- onClick = {
- // don't do anything if clicking on the current
- // destination because this results in some ugly animations.
- if (currentDestination?.route == item.destination.route) {
- return@NavigationBarItem
- }
-
- navController.navigate(item.destination.route) {
- // Pop up to the start destination of the graph to
- // avoid building up a large stack of destinations
- // on the back stack as users select items
- popUpTo(navController.graph.findStartDestination().id) {
- saveState = true
- }
- // Avoid multiple copies of the same destination when
- // reselecting the same item
- launchSingleTop = true
- // Restore state when re-selecting a previously selected item
- restoreState = true
- }
- },
- )
- }
- }
- }
- }
- }
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/home/HomeTab.kt b/app/src/main/java/io/github/sds100/keymapper/home/HomeTab.kt
deleted file mode 100644
index 264083953c..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/home/HomeTab.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package io.github.sds100.keymapper.home
-
-/**
- * Created by sds100 on 02/04/2021.
- */
-enum class HomeTab {
- KEY_EVENTS,
- FINGERPRINT_MAPS,
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/home/HomeViewModel.kt b/app/src/main/java/io/github/sds100/keymapper/home/HomeViewModel.kt
index 6044fad6cf..0c2f3dc906 100644
--- a/app/src/main/java/io/github/sds100/keymapper/home/HomeViewModel.kt
+++ b/app/src/main/java/io/github/sds100/keymapper/home/HomeViewModel.kt
@@ -1,44 +1,22 @@
package io.github.sds100.keymapper.home
-import android.os.Build
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.BubbleChart
-import androidx.compose.material.icons.outlined.Keyboard
-import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.viewModelScope
-import io.github.sds100.keymapper.Constants
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.backup.BackupRestoreMappingsUseCase
-import io.github.sds100.keymapper.floating.ListFloatingLayoutsUseCase
-import io.github.sds100.keymapper.floating.ListFloatingLayoutsViewModel
-import io.github.sds100.keymapper.keymaps.KeyMapListViewModel
-import io.github.sds100.keymapper.keymaps.ListKeyMapsUseCase
-import io.github.sds100.keymapper.keymaps.PauseKeyMapsUseCase
-import io.github.sds100.keymapper.onboarding.OnboardingUseCase
-import io.github.sds100.keymapper.sorting.SortKeyMapsUseCase
-import io.github.sds100.keymapper.system.inputmethod.ShowInputMethodPickerUseCase
-import io.github.sds100.keymapper.trigger.SetupGuiKeyboardUseCase
-import io.github.sds100.keymapper.util.ui.DialogResponse
-import io.github.sds100.keymapper.util.ui.NavigationViewModel
-import io.github.sds100.keymapper.util.ui.NavigationViewModelImpl
-import io.github.sds100.keymapper.util.ui.PopupUi
-import io.github.sds100.keymapper.util.ui.PopupViewModel
-import io.github.sds100.keymapper.util.ui.PopupViewModelImpl
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-import io.github.sds100.keymapper.util.ui.showPopup
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
-
-/**
- * Created by sds100 on 18/01/21.
- */
-class HomeViewModel(
+import dagger.hilt.android.lifecycle.HiltViewModel
+import io.github.sds100.keymapper.base.backup.BackupRestoreMappingsUseCase
+import io.github.sds100.keymapper.base.home.BaseHomeViewModel
+import io.github.sds100.keymapper.base.home.ShowHomeScreenAlertsUseCase
+import io.github.sds100.keymapper.base.keymaps.ListKeyMapsUseCase
+import io.github.sds100.keymapper.base.keymaps.PauseKeyMapsUseCase
+import io.github.sds100.keymapper.base.onboarding.OnboardingUseCase
+import io.github.sds100.keymapper.base.sorting.SortKeyMapsUseCase
+import io.github.sds100.keymapper.base.system.inputmethod.ShowInputMethodPickerUseCase
+import io.github.sds100.keymapper.base.trigger.SetupGuiKeyboardUseCase
+import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider
+import io.github.sds100.keymapper.base.utils.ui.DialogProvider
+import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
+import javax.inject.Inject
+
+@HiltViewModel
+class HomeViewModel @Inject constructor(
private val listKeyMaps: ListKeyMapsUseCase,
private val pauseKeyMaps: PauseKeyMapsUseCase,
private val backupRestore: BackupRestoreMappingsUseCase,
@@ -47,178 +25,19 @@ class HomeViewModel(
resourceProvider: ResourceProvider,
private val setupGuiKeyboard: SetupGuiKeyboardUseCase,
private val sortKeyMaps: SortKeyMapsUseCase,
- private val listFloatingLayouts: ListFloatingLayoutsUseCase,
private val showInputMethodPickerUseCase: ShowInputMethodPickerUseCase,
-) : ViewModel(),
- ResourceProvider by resourceProvider,
- PopupViewModel by PopupViewModelImpl(),
- NavigationViewModel by NavigationViewModelImpl() {
-
- val navBarItems: StateFlow> =
- combine(
- listFloatingLayouts.showFloatingLayouts,
- onboarding.hasViewedAdvancedTriggers,
- transform = ::buildNavBarItems,
- )
- .stateIn(
- viewModelScope,
- SharingStarted.Eagerly,
- buildNavBarItems(
- showFloatingLayouts = false,
- viewedAdvancedTriggers = false,
- ),
- )
-
- val keyMapListViewModel by lazy {
- KeyMapListViewModel(
- viewModelScope,
- listKeyMaps,
- resourceProvider,
- setupGuiKeyboard,
- sortKeyMaps,
- showAlertsUseCase,
- pauseKeyMaps,
- backupRestore,
- showInputMethodPickerUseCase,
- onboarding,
- )
- }
-
- val listFloatingLayoutsViewModel by lazy {
- ListFloatingLayoutsViewModel(
- viewModelScope,
- listFloatingLayouts,
- resourceProvider,
- )
- }
-
- init {
- viewModelScope.launch {
- onboarding.showWhatsNew.collect { showWhatsNew ->
- if (showWhatsNew) {
- showWhatsNewDialog()
- }
- }
- }
-
- viewModelScope.launch {
- if (setupGuiKeyboard.isInstalled.first() && !setupGuiKeyboard.isCompatibleVersion.first()) {
- showUpgradeGuiKeyboardDialog()
- }
- }
- }
-
- private fun buildNavBarItems(
- showFloatingLayouts: Boolean,
- viewedAdvancedTriggers: Boolean,
- ): List {
- val items = mutableListOf()
- items.add(
- HomeNavBarItem(
- HomeDestination.KeyMaps,
- getString(R.string.home_nav_bar_key_maps),
- icon = Icons.Outlined.Keyboard,
- badge = null,
- ),
- )
-
- if (showFloatingLayouts && Build.VERSION.SDK_INT >= Constants.MIN_API_FLOATING_BUTTONS) {
- items.add(
- HomeNavBarItem(
- HomeDestination.FloatingButtons,
- getString(R.string.home_nav_bar_floating_buttons),
- icon = Icons.Outlined.BubbleChart,
- badge = if (viewedAdvancedTriggers) {
- null
- } else {
- getString(R.string.button_advanced_triggers_badge)
- },
- ),
- )
- }
-
- return items
- }
-
- private suspend fun showWhatsNewDialog() {
- val dialog = PopupUi.Dialog(
- title = getString(R.string.whats_new),
- message = onboarding.getWhatsNewText(),
- positiveButtonText = getString(R.string.pos_ok),
- neutralButtonText = getString(R.string.neutral_changelog),
- )
-
- // don't return if they dismiss the dialog because this is common behaviour.
- val response = showPopup("whats-new", dialog)
-
- if (response == DialogResponse.NEUTRAL) {
- showPopup("url_changelog", PopupUi.OpenUrl(getString(R.string.url_changelog)))
- }
-
- onboarding.showedWhatsNew()
- }
-
- private suspend fun showUpgradeGuiKeyboardDialog() {
- val dialog = PopupUi.Dialog(
- title = getString(R.string.dialog_upgrade_gui_keyboard_title),
- message = getString(R.string.dialog_upgrade_gui_keyboard_message),
- positiveButtonText = getString(R.string.dialog_upgrade_gui_keyboard_positive),
- negativeButtonText = getString(R.string.dialog_upgrade_gui_keyboard_neutral),
- )
-
- val response = showPopup("upgrade_gui_keyboard", dialog)
-
- if (response == DialogResponse.POSITIVE) {
- showPopup(
- "gui_keyboard_play_store",
- PopupUi.OpenUrl(getString(R.string.url_play_store_keymapper_gui_keyboard)),
- )
- }
- }
-
- @Suppress("UNCHECKED_CAST")
- class Factory(
- private val listKeyMaps: ListKeyMapsUseCase,
- private val pauseMappings: PauseKeyMapsUseCase,
- private val backupRestore: BackupRestoreMappingsUseCase,
- private val showAlertsUseCase: ShowHomeScreenAlertsUseCase,
- private val onboarding: OnboardingUseCase,
- private val resourceProvider: ResourceProvider,
- private val setupGuiKeyboard: SetupGuiKeyboardUseCase,
- private val sortKeyMaps: SortKeyMapsUseCase,
- private val listFloatingLayouts: ListFloatingLayoutsUseCase,
- private val showInputMethodPickerUseCase: ShowInputMethodPickerUseCase,
- ) : ViewModelProvider.NewInstanceFactory() {
-
- override fun create(modelClass: Class): T = HomeViewModel(
- listKeyMaps,
- pauseMappings,
- backupRestore,
- showAlertsUseCase,
- onboarding,
- resourceProvider,
- setupGuiKeyboard,
- sortKeyMaps,
- listFloatingLayouts,
- showInputMethodPickerUseCase,
- ) as T
- }
-}
-
-enum class SelectedKeyMapsEnabled {
- ALL,
- NONE,
- MIXED,
-}
-
-data class HomeWarningListItem(
- val id: String,
- val text: String,
-)
-
-data class HomeNavBarItem(
- val destination: HomeDestination,
- val label: String,
- val icon: ImageVector,
- val badge: String? = null,
+ navigationProvider: NavigationProvider,
+ dialogProvider: DialogProvider,
+) : BaseHomeViewModel(
+ listKeyMaps,
+ pauseKeyMaps,
+ backupRestore,
+ showAlertsUseCase,
+ onboarding,
+ resourceProvider,
+ setupGuiKeyboard,
+ sortKeyMaps,
+ showInputMethodPickerUseCase,
+ navigationProvider,
+ dialogProvider,
)
diff --git a/app/src/main/java/io/github/sds100/keymapper/keymaps/ClickType.kt b/app/src/main/java/io/github/sds100/keymapper/keymaps/ClickType.kt
deleted file mode 100644
index 3a259bd68e..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/keymaps/ClickType.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package io.github.sds100.keymapper.keymaps
-
-/**
- * Created by sds100 on 21/02/2021.
- */
-enum class ClickType {
- SHORT_PRESS,
- LONG_PRESS,
- DOUBLE_PRESS,
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/keymaps/ConfigKeyMapFragment.kt b/app/src/main/java/io/github/sds100/keymapper/keymaps/ConfigKeyMapFragment.kt
deleted file mode 100644
index 20e9fc683e..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/keymaps/ConfigKeyMapFragment.kt
+++ /dev/null
@@ -1,113 +0,0 @@
-package io.github.sds100.keymapper.keymaps
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.WindowInsetsSides
-import androidx.compose.foundation.layout.add
-import androidx.compose.foundation.layout.displayCutout
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.only
-import androidx.compose.foundation.layout.systemBars
-import androidx.compose.foundation.layout.windowInsetsPadding
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.ViewCompositionStrategy
-import androidx.fragment.app.Fragment
-import androidx.navigation.findNavController
-import androidx.navigation.fragment.navArgs
-import androidx.navigation.navGraphViewModels
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.compose.KeyMapperTheme
-import io.github.sds100.keymapper.databinding.FragmentComposeBinding
-import io.github.sds100.keymapper.util.Inject
-import io.github.sds100.keymapper.util.ui.setupNavigation
-import io.github.sds100.keymapper.util.ui.showPopups
-
-class ConfigKeyMapFragment : Fragment() {
-
- private val args by navArgs()
-
- private val viewModel: ConfigKeyMapViewModel by navGraphViewModels(R.id.nav_config_keymap) {
- Inject.configKeyMapViewModel(requireContext())
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- // only load the keymap if opening this fragment for the first time
- if (savedInstanceState == null) {
- args.keyMapUid.also { keyMapUid ->
- if (keyMapUid == null) {
- viewModel.loadNewKeymap(
- args.newFloatingButtonTriggerKey,
- groupUid = args.groupUid,
- )
- } else {
- viewModel.loadKeyMap(keyMapUid)
- }
- }
-
- if (args.showAdvancedTriggers) {
- viewModel.configTriggerViewModel.showAdvancedTriggersBottomSheet = true
- }
- }
-
- viewModel.configTriggerViewModel.setupNavigation(this)
- viewModel.configActionsViewModel.setupNavigation(this)
- viewModel.configConstraintsViewModel.setupNavigation(this)
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?,
- ): View {
- FragmentComposeBinding.inflate(inflater, container, false).apply {
- composeView.apply {
- // Dispose of the Composition when the view's LifecycleOwner
- // is destroyed
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- setContent {
- KeyMapperTheme {
- ConfigKeyMapScreen(
- modifier = Modifier
- .windowInsetsPadding(
- WindowInsets.systemBars.only(sides = WindowInsetsSides.Horizontal)
- .add(WindowInsets.displayCutout.only(sides = WindowInsetsSides.Horizontal)),
- )
- .fillMaxSize(),
- viewModel = viewModel,
- navigateBack = findNavController()::navigateUp,
- )
- }
- }
- }
- return this.root
- }
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- viewModel.configTriggerViewModel.showPopups(this, view)
- viewModel.configTriggerViewModel.optionsViewModel.showPopups(this, view)
- viewModel.configActionsViewModel.showPopups(this, view)
- viewModel.configConstraintsViewModel.showPopups(this, view)
- }
-
- override fun onSaveInstanceState(outState: Bundle) {
- viewModel.saveState(outState)
-
- super.onSaveInstanceState(outState)
- }
-
- override fun onViewStateRestored(savedInstanceState: Bundle?) {
- super.onViewStateRestored(savedInstanceState)
-
- savedInstanceState ?: return
-
- viewModel.restoreState(savedInstanceState)
- }
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/keymaps/ConfigKeyMapScreen.kt b/app/src/main/java/io/github/sds100/keymapper/keymaps/ConfigKeyMapScreen.kt
index f70a0998e8..61e36a3624 100644
--- a/app/src/main/java/io/github/sds100/keymapper/keymaps/ConfigKeyMapScreen.kt
+++ b/app/src/main/java/io/github/sds100/keymapper/keymaps/ConfigKeyMapScreen.kt
@@ -1,76 +1,26 @@
package io.github.sds100.keymapper.keymaps
-import androidx.activity.compose.BackHandler
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.BoxWithConstraints
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.displayCutoutPadding
-import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.pager.HorizontalPager
-import androidx.compose.foundation.pager.rememberPagerState
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.rounded.ArrowBack
-import androidx.compose.material.icons.automirrored.rounded.HelpOutline
-import androidx.compose.material.icons.rounded.Check
-import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.BottomAppBar
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.ExtendedFloatingActionButton
-import androidx.compose.material3.FloatingActionButtonDefaults
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedCard
-import androidx.compose.material3.PrimaryScrollableTabRow
-import androidx.compose.material3.PrimaryTabRow
-import androidx.compose.material3.Scaffold
-import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
-import androidx.compose.material3.Switch
-import androidx.compose.material3.Tab
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalUriHandler
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Devices
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.canopas.lib.showcase.IntroShowcase
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.actions.ActionsScreen
-import io.github.sds100.keymapper.compose.KeyMapperTheme
-import io.github.sds100.keymapper.constraints.ConstraintsScreen
-import io.github.sds100.keymapper.onboarding.OnboardingTapTarget
+import io.github.sds100.keymapper.base.actions.ActionsScreen
+import io.github.sds100.keymapper.base.constraints.ConstraintsScreen
+import io.github.sds100.keymapper.base.keymaps.BaseConfigKeyMapScreen
+import io.github.sds100.keymapper.base.keymaps.KeyMapOptionsScreen
+import io.github.sds100.keymapper.base.utils.ui.UnsavedChangesDialog
import io.github.sds100.keymapper.trigger.TriggerScreen
-import io.github.sds100.keymapper.util.ui.compose.KeyMapperTapTarget
-import io.github.sds100.keymapper.util.ui.compose.keyMapperShowcaseStyle
-import io.github.sds100.keymapper.util.ui.compose.openUriSafe
-import kotlinx.coroutines.launch
@Composable
fun ConfigKeyMapScreen(
modifier: Modifier = Modifier,
viewModel: ConfigKeyMapViewModel,
- navigateBack: () -> Unit,
) {
val isKeyMapEnabled by viewModel.isEnabled.collectAsStateWithLifecycle()
val showActionTapTarget by viewModel.showActionsTapTarget.collectAsStateWithLifecycle()
@@ -81,16 +31,16 @@ fun ConfigKeyMapScreen(
var showBackDialog by rememberSaveable { mutableStateOf(false) }
if (showBackDialog) {
- BackDialog(
+ UnsavedChangesDialog(
onDismiss = { showBackDialog = false },
onDiscardClick = {
showBackDialog = false
- navigateBack()
+ viewModel.onBackClick()
},
)
}
- ConfigKeyMapScreen(
+ BaseConfigKeyMapScreen(
modifier = modifier,
isKeyMapEnabled = isKeyMapEnabled,
onKeyMapEnabledChange = viewModel::onEnabledChanged,
@@ -117,13 +67,10 @@ fun ConfigKeyMapScreen(
if (viewModel.isKeyMapEdited) {
showBackDialog = true
} else {
- navigateBack()
+ viewModel.onBackClick()
}
},
- onDoneClick = {
- viewModel.save()
- navigateBack()
- },
+ onDoneClick = viewModel::onDoneClick,
snackbarHostState = snackbarHostState,
showActionTapTarget = showActionTapTarget,
onActionTapTargetCompleted = viewModel::onActionTapTargetCompleted,
@@ -132,571 +79,3 @@ fun ConfigKeyMapScreen(
onSkipTutorialClick = viewModel::onSkipTutorialClick,
)
}
-
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-private fun ConfigKeyMapScreen(
- modifier: Modifier = Modifier,
- isKeyMapEnabled: Boolean,
- onKeyMapEnabledChange: (Boolean) -> Unit = {},
- triggerScreen: @Composable () -> Unit,
- actionScreen: @Composable () -> Unit,
- constraintsScreen: @Composable () -> Unit,
- optionsScreen: @Composable () -> Unit,
- onBackClick: () -> Unit = {},
- onDoneClick: () -> Unit = {},
- snackbarHostState: SnackbarHostState = SnackbarHostState(),
- showActionTapTarget: Boolean = false,
- onActionTapTargetCompleted: () -> Unit = {},
- showConstraintTapTarget: Boolean = false,
- onConstraintTapTargetCompleted: () -> Unit = {},
- onSkipTutorialClick: () -> Unit = {},
-) {
- val scope = rememberCoroutineScope()
- val triggerHelpUrl = stringResource(R.string.url_trigger_guide)
- val actionsHelpUrl = stringResource(R.string.url_action_guide)
- val constraintsHelpUrl = stringResource(R.string.url_constraints_guide)
- val optionsHelpUrl = stringResource(R.string.url_trigger_options_guide)
-
- var currentTab: ConfigKeyMapTab? by remember { mutableStateOf(null) }
- val uriHandler = LocalUriHandler.current
- val ctx = LocalContext.current
-
- BackHandler(onBack = onBackClick)
-
- Scaffold(
- modifier.displayCutoutPadding(),
- snackbarHost = { SnackbarHost(snackbarHostState) },
- bottomBar = {
- ConfigKeyMapAppBar(
- isKeyMapEnabled = isKeyMapEnabled,
- onKeyMapEnabledChange = onKeyMapEnabledChange,
- onBackClick = onBackClick,
- onDoneClick = onDoneClick,
- showHelpButton = currentTab == ConfigKeyMapTab.TRIGGER ||
- currentTab == ConfigKeyMapTab.ACTIONS ||
- currentTab == ConfigKeyMapTab.CONSTRAINTS ||
- currentTab == ConfigKeyMapTab.OPTIONS,
- onHelpClick = {
- val url = when (currentTab) {
- ConfigKeyMapTab.TRIGGER -> triggerHelpUrl
- ConfigKeyMapTab.ACTIONS -> actionsHelpUrl
- ConfigKeyMapTab.CONSTRAINTS -> constraintsHelpUrl
- ConfigKeyMapTab.OPTIONS -> optionsHelpUrl
- else -> return@ConfigKeyMapAppBar
- }
-
- if (url.isNotEmpty()) {
- uriHandler.openUriSafe(ctx, url)
- }
- },
- )
- },
- ) { innerPadding ->
- BoxWithConstraints(modifier = Modifier.padding(innerPadding)) {
- val tabs = determineTabs(maxWidth, maxHeight)
- val isVerticalTwoScreen = maxWidth < 720.dp
- val pagerState = rememberPagerState(pageCount = { tabs.size }, initialPage = 0)
- currentTab = tabs.getOrNull(pagerState.targetPage)
-
- Column(Modifier.fillMaxSize()) {
- if (tabs.size > 1) {
- @Composable
- fun Tabs() {
- for ((index, tab) in tabs.withIndex()) {
- val tapTarget: OnboardingTapTarget? = when {
- showActionTapTarget && tab == ConfigKeyMapTab.ACTIONS -> OnboardingTapTarget.CHOOSE_ACTION
- showConstraintTapTarget && (tab == ConfigKeyMapTab.CONSTRAINTS || tab == ConfigKeyMapTab.CONSTRAINTS_AND_OPTIONS) -> OnboardingTapTarget.CHOOSE_CONSTRAINT
- else -> null
- }
-
- IntroShowcase(
- showIntroShowCase = tapTarget != null,
- onShowCaseCompleted = if (tapTarget == OnboardingTapTarget.CHOOSE_ACTION) onActionTapTargetCompleted else onConstraintTapTargetCompleted,
- dismissOnClickOutside = true,
- ) {
- var tabModifier: Modifier = Modifier
-
- if (tapTarget != null) {
- tabModifier = tabModifier.introShowCaseTarget(
- index = 0,
- style = keyMapperShowcaseStyle(),
- ) {
- KeyMapperTapTarget(
- tapTarget = tapTarget,
- onSkipClick = onSkipTutorialClick,
- )
- }
- }
-
- Tab(
- modifier = tabModifier,
- selected = pagerState.targetPage == index,
- text = {
- Text(
- text = getTabTitle(tab),
- maxLines = 1,
- )
- },
- onClick = {
- scope.launch {
- pagerState.animateScrollToPage(
- tabs.indexOf(tab),
- )
- }
- },
- )
- }
- }
- }
-
- if (this@BoxWithConstraints.maxWidth < 500.dp) {
- PrimaryScrollableTabRow(
- selectedTabIndex = pagerState.targetPage,
- divider = {},
- edgePadding = 16.dp,
- contentColor = MaterialTheme.colorScheme.onSurface,
- ) {
- Tabs()
- }
- } else {
- PrimaryTabRow(
- selectedTabIndex = pagerState.targetPage,
- divider = {},
- contentColor = MaterialTheme.colorScheme.onSurface,
- ) {
- Tabs()
- }
- }
- }
-
- HorizontalPager(
- modifier = Modifier.fillMaxSize(),
- state = pagerState,
- ) { pageIndex ->
- when (tabs[pageIndex]) {
- ConfigKeyMapTab.TRIGGER -> triggerScreen()
- ConfigKeyMapTab.ACTIONS -> actionScreen()
- ConfigKeyMapTab.CONSTRAINTS -> constraintsScreen()
- ConfigKeyMapTab.OPTIONS -> optionsScreen()
- ConfigKeyMapTab.TRIGGER_AND_ACTIONS -> {
- if (isVerticalTwoScreen) {
- VerticalTwoScreens(
- topTitle = stringResource(R.string.tab_trigger),
- topHelpUrl = triggerHelpUrl,
- topScreen = triggerScreen,
- bottomTitle = stringResource(R.string.tab_actions),
- bottomHelpUrl = actionsHelpUrl,
- bottomScreen = actionScreen,
- )
- } else {
- HorizontalTwoScreens(
- leftTitle = stringResource(R.string.tab_trigger),
- leftHelpUrl = triggerHelpUrl,
- leftScreen = triggerScreen,
- rightTitle = stringResource(R.string.tab_actions),
- rightHelpUrl = actionsHelpUrl,
- rightScreen = actionScreen,
- )
- }
- }
-
- ConfigKeyMapTab.CONSTRAINTS_AND_OPTIONS -> {
- if (isVerticalTwoScreen) {
- VerticalTwoScreens(
- topTitle = stringResource(R.string.tab_constraints),
- topHelpUrl = constraintsHelpUrl,
- topScreen = constraintsScreen,
- bottomTitle = stringResource(R.string.tab_options),
- bottomHelpUrl = optionsHelpUrl,
- bottomScreen = optionsScreen,
- )
- } else {
- HorizontalTwoScreens(
- leftTitle = stringResource(R.string.tab_constraints),
- leftHelpUrl = constraintsHelpUrl,
- leftScreen = constraintsScreen,
- rightTitle = stringResource(R.string.tab_options),
- rightHelpUrl = optionsHelpUrl,
- rightScreen = optionsScreen,
- )
- }
- }
-
- ConfigKeyMapTab.ALL -> FourScreens(
- topLeftTitle = stringResource(R.string.tab_trigger),
- topLeftHelpUrl = triggerHelpUrl,
- topLeftScreen = triggerScreen,
- topRightTitle = stringResource(R.string.tab_actions),
- topRightHelpUrl = actionsHelpUrl,
- topRightScreen = actionScreen,
- bottomLeftTitle = stringResource(R.string.tab_constraints),
- bottomLeftHelpUrl = constraintsHelpUrl,
- bottomLeftScreen = constraintsScreen,
- bottomRightTitle = stringResource(R.string.tab_options),
- bottomRightHelpUrl = optionsHelpUrl,
- bottomRightScreen = optionsScreen,
- )
- }
- }
- }
- }
- }
-}
-
-@Composable
-private fun ConfigKeyMapAppBar(
- modifier: Modifier = Modifier,
- isKeyMapEnabled: Boolean,
- onKeyMapEnabledChange: (Boolean) -> Unit = {},
- showHelpButton: Boolean,
- onHelpClick: () -> Unit,
- onBackClick: () -> Unit,
- onDoneClick: () -> Unit,
-) {
- BottomAppBar(
- modifier = modifier,
- floatingActionButton = {
- ExtendedFloatingActionButton(
- onClick = onDoneClick,
- text = { Text(stringResource(R.string.button_done)) },
- icon = {
- Icon(Icons.Rounded.Check, stringResource(R.string.button_done))
- },
- elevation = FloatingActionButtonDefaults.bottomAppBarFabElevation(),
- )
- },
- actions = {
- IconButton(onClick = onBackClick) {
- Icon(Icons.AutoMirrored.Rounded.ArrowBack, stringResource(R.string.action_go_back))
- }
-
- Spacer(modifier = Modifier.width(8.dp))
-
- val text = if (isKeyMapEnabled) {
- stringResource(R.string.switch_enabled)
- } else {
- stringResource(R.string.switch_disabled)
- }
-
- Text(
- text = text,
- style = MaterialTheme.typography.labelLarge,
- )
-
- Spacer(modifier = Modifier.width(16.dp))
-
- Switch(
- checked = isKeyMapEnabled,
- onCheckedChange = onKeyMapEnabledChange,
- )
-
- Spacer(modifier = Modifier.weight(1f))
-
- if (showHelpButton) {
- IconButton(onClick = onHelpClick) {
- Icon(
- Icons.AutoMirrored.Rounded.HelpOutline,
- stringResource(R.string.action_help),
- )
- }
- }
-
- Spacer(modifier = Modifier.width(8.dp))
- },
- )
-}
-
-@Composable
-private fun VerticalTwoScreens(
- modifier: Modifier = Modifier,
- topTitle: String,
- topHelpUrl: String,
- topScreen: @Composable () -> Unit,
- bottomTitle: String,
- bottomHelpUrl: String,
- bottomScreen: @Composable () -> Unit,
-) {
- Column(modifier = modifier) {
- Spacer(modifier = Modifier.height(8.dp))
- ScreenCard(
- Modifier
- .weight(1f)
- .fillMaxWidth()
- .padding(horizontal = 8.dp),
- topTitle,
- topHelpUrl,
- topScreen,
- )
- Spacer(modifier = Modifier.height(8.dp))
- ScreenCard(
- Modifier
- .weight(1f)
- .fillMaxWidth()
- .padding(horizontal = 8.dp),
- bottomTitle,
- bottomHelpUrl,
- bottomScreen,
- )
- Spacer(modifier = Modifier.height(8.dp))
- }
-}
-
-@Composable
-private fun HorizontalTwoScreens(
- modifier: Modifier = Modifier,
- leftTitle: String,
- leftHelpUrl: String,
- leftScreen: @Composable () -> Unit,
- rightTitle: String,
- rightHelpUrl: String,
- rightScreen: @Composable () -> Unit,
-) {
- Column(modifier = modifier) {
- Spacer(modifier = Modifier.height(8.dp))
- Row(Modifier.weight(1f)) {
- ScreenCard(
- Modifier
- .weight(1f)
- .fillMaxHeight()
- .padding(start = 8.dp),
- leftTitle,
- leftHelpUrl,
- leftScreen,
- )
- Spacer(modifier = Modifier.width(8.dp))
- ScreenCard(
- Modifier
- .weight(1f)
- .fillMaxHeight()
- .padding(end = 8.dp),
- rightTitle,
- rightHelpUrl,
- rightScreen,
- )
- }
- Spacer(modifier = Modifier.height(8.dp))
- }
-}
-
-@Composable
-fun FourScreens(
- modifier: Modifier = Modifier,
- topLeftTitle: String,
- topLeftHelpUrl: String,
- topLeftScreen: @Composable () -> Unit,
- topRightTitle: String,
- topRightHelpUrl: String,
- topRightScreen: @Composable () -> Unit,
- bottomLeftTitle: String,
- bottomLeftHelpUrl: String,
- bottomLeftScreen: @Composable () -> Unit,
- bottomRightTitle: String,
- bottomRightHelpUrl: String,
- bottomRightScreen: @Composable () -> Unit,
-) {
- Column(modifier = modifier) {
- Spacer(modifier = Modifier.height(8.dp))
- Row(Modifier.weight(1f)) {
- ScreenCard(
- Modifier
- .weight(1f)
- .fillMaxHeight()
- .padding(start = 8.dp),
- topLeftTitle,
- topLeftHelpUrl,
- topLeftScreen,
- )
- Spacer(modifier = Modifier.width(8.dp))
- ScreenCard(
- Modifier
- .weight(1f)
- .fillMaxHeight()
- .padding(end = 8.dp),
- topRightTitle,
- topRightHelpUrl,
- topRightScreen,
- )
- }
- Spacer(modifier = Modifier.height(8.dp))
- Row(Modifier.weight(1f)) {
- ScreenCard(
- Modifier
- .weight(1f)
- .fillMaxHeight()
- .padding(start = 8.dp),
- bottomLeftTitle,
- bottomLeftHelpUrl,
- bottomLeftScreen,
- )
- Spacer(modifier = Modifier.width(8.dp))
- ScreenCard(
- Modifier
- .weight(1f)
- .fillMaxHeight()
- .padding(end = 8.dp),
- bottomRightTitle,
- bottomRightHelpUrl,
- bottomRightScreen,
- )
- }
- Spacer(modifier = Modifier.height(8.dp))
- }
-}
-
-@Composable
-private fun ScreenCard(
- modifier: Modifier = Modifier,
- title: String,
- helpUrl: String,
- screen: @Composable () -> Unit,
-) {
- val uriHandler = LocalUriHandler.current
- val ctx = LocalContext.current
-
- OutlinedCard(modifier = modifier) {
- Column {
- Row(
- modifier = Modifier.fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween,
- ) {
- Text(
- modifier = Modifier.padding(horizontal = 16.dp),
- text = title,
- style = MaterialTheme.typography.titleSmall,
- color = MaterialTheme.colorScheme.primary,
- )
-
- IconButton(onClick = { uriHandler.openUriSafe(ctx, helpUrl) }) {
- Icon(
- Icons.AutoMirrored.Rounded.HelpOutline,
- contentDescription = stringResource(R.string.button_help),
- )
- }
- }
-
- screen()
- }
- }
-}
-
-@Composable
-private fun BackDialog(
- onDismiss: () -> Unit,
- onDiscardClick: () -> Unit,
-) {
- AlertDialog(
- onDismissRequest = onDismiss,
- title = { Text(stringResource(R.string.dialog_title_unsaved_changes)) },
- text = { Text(stringResource(R.string.dialog_message_unsaved_changes)) },
- confirmButton = {
- TextButton(onClick = onDiscardClick) { Text(stringResource(R.string.pos_discard_changes)) }
- },
- dismissButton = {
- TextButton(onClick = onDismiss) { Text(stringResource(R.string.neg_keep_editing)) }
- },
- )
-}
-
-private fun determineTabs(maxWidth: Dp, maxHeight: Dp): List {
- return when {
- maxWidth >= 800.dp && maxHeight >= 800.dp -> listOf(ConfigKeyMapTab.ALL)
-
- (maxWidth >= 1000.dp && maxHeight >= 450.dp) ||
- (maxWidth >= 450.dp && maxHeight >= 1000.dp) -> listOf(
- ConfigKeyMapTab.TRIGGER_AND_ACTIONS,
- ConfigKeyMapTab.CONSTRAINTS_AND_OPTIONS,
- )
-
- else -> listOf(
- ConfigKeyMapTab.TRIGGER,
- ConfigKeyMapTab.ACTIONS,
- ConfigKeyMapTab.CONSTRAINTS,
- ConfigKeyMapTab.OPTIONS,
- )
- }
-}
-
-@Composable
-private fun getTabTitle(tab: ConfigKeyMapTab): String {
- return when (tab) {
- ConfigKeyMapTab.TRIGGER -> stringResource(R.string.tab_trigger)
- ConfigKeyMapTab.ACTIONS -> stringResource(R.string.tab_actions)
- ConfigKeyMapTab.CONSTRAINTS -> stringResource(R.string.tab_constraints)
- ConfigKeyMapTab.OPTIONS -> stringResource(R.string.tab_options)
- ConfigKeyMapTab.TRIGGER_AND_ACTIONS -> stringResource(R.string.tab_trigger_and_actions)
- ConfigKeyMapTab.CONSTRAINTS_AND_OPTIONS -> stringResource(R.string.tab_constraints_and_more)
- ConfigKeyMapTab.ALL -> ""
- }
-}
-
-private enum class ConfigKeyMapTab {
- TRIGGER,
- ACTIONS,
- CONSTRAINTS,
- OPTIONS,
- TRIGGER_AND_ACTIONS,
- CONSTRAINTS_AND_OPTIONS,
- ALL,
-}
-
-@Preview(device = Devices.PIXEL, showSystemUi = true)
-@Composable
-private fun SmallScreenPreview() {
- KeyMapperTheme {
- ConfigKeyMapScreen(
- modifier = Modifier.fillMaxSize(),
- isKeyMapEnabled = false,
- triggerScreen = {},
- actionScreen = {},
- constraintsScreen = {},
- optionsScreen = {},
- )
- }
-}
-
-@Preview(device = Devices.NEXUS_7_2013, showSystemUi = true)
-@Composable
-private fun MediumScreenPreview() {
- KeyMapperTheme {
- ConfigKeyMapScreen(
- modifier = Modifier.fillMaxSize(),
- isKeyMapEnabled = true,
- triggerScreen = {},
- actionScreen = {},
- constraintsScreen = {},
- optionsScreen = {},
- )
- }
-}
-
-@Preview(device = Devices.FOLDABLE, showSystemUi = true)
-@Composable
-private fun MediumScreenLandscapePreview() {
- KeyMapperTheme {
- ConfigKeyMapScreen(
- modifier = Modifier.fillMaxSize(),
- isKeyMapEnabled = true,
- triggerScreen = {},
- actionScreen = {},
- constraintsScreen = {},
- optionsScreen = {},
- )
- }
-}
-
-@Preview(device = Devices.NEXUS_10, showSystemUi = true)
-@Composable
-private fun LargeScreenPreview() {
- KeyMapperTheme {
- ConfigKeyMapScreen(
- modifier = Modifier.fillMaxSize(),
- isKeyMapEnabled = true,
- triggerScreen = {},
- actionScreen = {},
- constraintsScreen = {},
- optionsScreen = {},
- )
- }
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/keymaps/ConfigKeyMapViewModel.kt b/app/src/main/java/io/github/sds100/keymapper/keymaps/ConfigKeyMapViewModel.kt
index f86292155e..046f5cee1c 100644
--- a/app/src/main/java/io/github/sds100/keymapper/keymaps/ConfigKeyMapViewModel.kt
+++ b/app/src/main/java/io/github/sds100/keymapper/keymaps/ConfigKeyMapViewModel.kt
@@ -1,181 +1,81 @@
package io.github.sds100.keymapper.keymaps
-import android.os.Bundle
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
-import io.github.sds100.keymapper.actions.ConfigActionsViewModel
-import io.github.sds100.keymapper.actions.CreateActionUseCase
-import io.github.sds100.keymapper.actions.TestActionUseCase
-import io.github.sds100.keymapper.constraints.ConfigConstraintsViewModel
-import io.github.sds100.keymapper.onboarding.OnboardingTapTarget
-import io.github.sds100.keymapper.onboarding.OnboardingUseCase
-import io.github.sds100.keymapper.purchasing.PurchasingManager
+import dagger.hilt.android.lifecycle.HiltViewModel
+import io.github.sds100.keymapper.base.actions.ConfigActionsViewModel
+import io.github.sds100.keymapper.base.actions.CreateActionUseCase
+import io.github.sds100.keymapper.base.actions.TestActionUseCase
+import io.github.sds100.keymapper.base.constraints.ConfigConstraintsViewModel
+import io.github.sds100.keymapper.base.keymaps.BaseConfigKeyMapViewModel
+import io.github.sds100.keymapper.base.keymaps.ConfigKeyMapUseCase
+import io.github.sds100.keymapper.base.keymaps.CreateKeyMapShortcutUseCase
+import io.github.sds100.keymapper.base.keymaps.DisplayKeyMapUseCase
+import io.github.sds100.keymapper.base.keymaps.FingerprintGesturesSupportedUseCase
+import io.github.sds100.keymapper.base.onboarding.OnboardingUseCase
+import io.github.sds100.keymapper.base.purchasing.PurchasingManager
+import io.github.sds100.keymapper.base.trigger.RecordTriggerUseCase
+import io.github.sds100.keymapper.base.trigger.SetupGuiKeyboardUseCase
+import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider
+import io.github.sds100.keymapper.base.utils.ui.DialogProvider
+import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
import io.github.sds100.keymapper.trigger.ConfigTriggerViewModel
-import io.github.sds100.keymapper.trigger.RecordTriggerUseCase
-import io.github.sds100.keymapper.trigger.SetupGuiKeyboardUseCase
-import io.github.sds100.keymapper.ui.utils.getJsonSerializable
-import io.github.sds100.keymapper.ui.utils.putJsonSerializable
-import io.github.sds100.keymapper.util.dataOrNull
-import io.github.sds100.keymapper.util.firstBlocking
-import io.github.sds100.keymapper.util.ifIsData
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
+import javax.inject.Inject
-/**
- * Created by sds100 on 22/11/20.
- */
-
-class ConfigKeyMapViewModel(
- private val config: ConfigKeyMapUseCase,
- private val testAction: TestActionUseCase,
- private val onboarding: OnboardingUseCase,
- private val recordTrigger: RecordTriggerUseCase,
- private val createKeyMapShortcut: CreateKeyMapShortcutUseCase,
- private val displayMapping: DisplayKeyMapUseCase,
+@HiltViewModel
+class ConfigKeyMapViewModel @Inject constructor(
+ display: DisplayKeyMapUseCase,
+ config: ConfigKeyMapUseCase,
+ onboarding: OnboardingUseCase,
createActionUseCase: CreateActionUseCase,
- resourceProvider: ResourceProvider,
+ testActionUseCase: TestActionUseCase,
+ recordTriggerUseCase: RecordTriggerUseCase,
+ createKeyMapShortcutUseCase: CreateKeyMapShortcutUseCase,
purchasingManager: PurchasingManager,
setupGuiKeyboardUseCase: SetupGuiKeyboardUseCase,
- fingerprintGesturesSupported: FingerprintGesturesSupportedUseCase,
-) : ViewModel(),
- ResourceProvider by resourceProvider {
-
- companion object {
- private const val STATE_KEY = "config_keymap"
- }
-
- val configActionsViewModel = ConfigActionsViewModel(
- viewModelScope,
- displayMapping,
- createActionUseCase,
- testAction,
- config,
- onboarding,
- resourceProvider,
- )
-
- val configTriggerViewModel = ConfigTriggerViewModel(
- viewModelScope,
- onboarding,
- config,
- recordTrigger,
- createKeyMapShortcut,
- displayMapping,
- resourceProvider,
- purchasingManager,
- setupGuiKeyboardUseCase,
- fingerprintGesturesSupported,
+ fingerprintGesturesSupportedUseCase: FingerprintGesturesSupportedUseCase,
+ resourceProvider: ResourceProvider,
+ navigationProvider: NavigationProvider,
+ dialogProvider: DialogProvider,
+) : BaseConfigKeyMapViewModel(
+ config = config,
+ onboarding = onboarding,
+ navigationProvider = navigationProvider,
+ dialogProvider = dialogProvider,
+) {
+ override val configActionsViewModel: ConfigActionsViewModel = ConfigActionsViewModel(
+ coroutineScope = viewModelScope,
+ displayAction = display,
+ createAction = createActionUseCase,
+ testAction = testActionUseCase,
+ config = config,
+ onboarding = onboarding,
+ resourceProvider = resourceProvider,
+ navigationProvider = navigationProvider,
+ dialogProvider = dialogProvider,
)
- val configConstraintsViewModel = ConfigConstraintsViewModel(
- viewModelScope,
- config,
- displayMapping,
- resourceProvider,
+ override val configTriggerViewModel: ConfigTriggerViewModel = ConfigTriggerViewModel(
+ coroutineScope = viewModelScope,
+ onboarding = onboarding,
+ config = config,
+ recordTrigger = recordTriggerUseCase,
+ createKeyMapShortcut = createKeyMapShortcutUseCase,
+ displayKeyMap = display,
+ purchasingManager = purchasingManager,
+ setupGuiKeyboard = setupGuiKeyboardUseCase,
+ fingerprintGesturesSupported = fingerprintGesturesSupportedUseCase,
+ resourceProvider = resourceProvider,
+ navigationProvider = navigationProvider,
+ dialogProvider = dialogProvider,
)
- val isEnabled: StateFlow = config.keyMap
- .map { state -> state.dataOrNull()?.isEnabled ?: true }
- .stateIn(viewModelScope, SharingStarted.Eagerly, true)
-
- val isKeyMapEdited: Boolean
- get() = config.isEdited
-
- val showActionsTapTarget: StateFlow =
- combine(
- onboarding.showTapTarget(OnboardingTapTarget.CHOOSE_ACTION),
- config.keyMap,
- ) { showTapTarget, keyMapState ->
- // Show the choose action tap target if they have recorded a key.
- showTapTarget && keyMapState.dataOrNull()?.trigger?.keys?.isNotEmpty() ?: false
- }.stateIn(viewModelScope, SharingStarted.Lazily, false)
-
- val showConstraintsTapTarget: StateFlow =
- combine(
- onboarding.showTapTarget(OnboardingTapTarget.CHOOSE_CONSTRAINT),
- config.keyMap,
- ) { showTapTarget, keyMapState ->
- // Show the choose constraint tap target if they have added an action.
- showTapTarget && keyMapState.dataOrNull()?.actionList?.isNotEmpty() ?: false
- }.stateIn(viewModelScope, SharingStarted.Lazily, false)
-
- fun save() = config.save()
-
- fun saveState(outState: Bundle) {
- config.keyMap.firstBlocking().ifIsData {
- outState.putJsonSerializable(STATE_KEY, it)
- }
- }
-
- fun restoreState(state: Bundle) {
- val keyMap = state.getJsonSerializable(STATE_KEY) ?: KeyMap()
- config.restoreState(keyMap)
- }
-
- fun loadNewKeymap(floatingButtonUid: String? = null, groupUid: String?) {
- config.loadNewKeyMap(groupUid)
- if (floatingButtonUid != null) {
- viewModelScope.launch {
- config.addFloatingButtonTriggerKey(floatingButtonUid)
- }
- }
- }
-
- fun loadKeyMap(uid: String) {
- viewModelScope.launch {
- config.loadKeyMap(uid)
- }
- }
-
- fun onEnabledChanged(enabled: Boolean) {
- config.setEnabled(enabled)
- }
-
- fun onActionTapTargetCompleted() {
- onboarding.completedTapTarget(OnboardingTapTarget.CHOOSE_ACTION)
- }
-
- fun onConstraintTapTargetCompleted() {
- onboarding.completedTapTarget(OnboardingTapTarget.CHOOSE_CONSTRAINT)
- }
-
- fun onSkipTutorialClick() {
- onboarding.skipTapTargetOnboarding()
- }
-
- class Factory(
- private val config: ConfigKeyMapUseCase,
- private val testAction: TestActionUseCase,
- private val onboard: OnboardingUseCase,
- private val recordTrigger: RecordTriggerUseCase,
- private val createKeyMapShortcut: CreateKeyMapShortcutUseCase,
- private val displayMapping: DisplayKeyMapUseCase,
- private val createActionUseCase: CreateActionUseCase,
- private val resourceProvider: ResourceProvider,
- private val purchasingManager: PurchasingManager,
- private val setupGuiKeyboardUseCase: SetupGuiKeyboardUseCase,
- private val fingerprintGesturesSupported: FingerprintGesturesSupportedUseCase,
- ) : ViewModelProvider.Factory {
-
- @Suppress("UNCHECKED_CAST")
- override fun create(modelClass: Class) = ConfigKeyMapViewModel(
- config,
- testAction,
- onboard,
- recordTrigger,
- createKeyMapShortcut,
- displayMapping,
- createActionUseCase,
- resourceProvider,
- purchasingManager,
- setupGuiKeyboardUseCase,
- fingerprintGesturesSupported,
- ) as T
- }
+ override val configConstraintsViewModel: ConfigConstraintsViewModel =
+ ConfigConstraintsViewModel(
+ coroutineScope = viewModelScope,
+ config = config,
+ displayConstraint = display,
+ resourceProvider = resourceProvider,
+ navigationProvider = navigationProvider,
+ dialogProvider = dialogProvider,
+ )
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/keymaps/CreateKeyMapShortcutActivity.kt b/app/src/main/java/io/github/sds100/keymapper/keymaps/CreateKeyMapShortcutActivity.kt
deleted file mode 100644
index 48d0e35783..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/keymaps/CreateKeyMapShortcutActivity.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-package io.github.sds100.keymapper.keymaps
-
-import android.os.Bundle
-import androidx.activity.SystemBarStyle
-import androidx.activity.compose.setContent
-import androidx.activity.enableEdgeToEdge
-import androidx.activity.viewModels
-import androidx.appcompat.app.AppCompatActivity
-import androidx.compose.ui.graphics.toArgb
-import androidx.lifecycle.Lifecycle
-import androidx.navigation.findNavController
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.ServiceLocator
-import io.github.sds100.keymapper.compose.ComposeColors
-import io.github.sds100.keymapper.compose.KeyMapperTheme
-import io.github.sds100.keymapper.system.permissions.RequestPermissionDelegate
-import io.github.sds100.keymapper.util.Inject
-import io.github.sds100.keymapper.util.launchRepeatOnLifecycle
-import kotlinx.coroutines.flow.collectLatest
-
-/**
- * Created by sds100 on 08/09/20.
- */
-
-class CreateKeyMapShortcutActivity : AppCompatActivity() {
-
- private val viewModel by viewModels {
- Inject.createActionShortcutViewModel(this)
- }
-
- private lateinit var requestPermissionDelegate: RequestPermissionDelegate
-
- override fun onCreate(savedInstanceState: Bundle?) {
- enableEdgeToEdge(
- statusBarStyle = SystemBarStyle.auto(
- ComposeColors.surfaceContainerLight.toArgb(),
- ComposeColors.surfaceContainerDark.toArgb(),
- ),
- navigationBarStyle = SystemBarStyle.auto(
- ComposeColors.surfaceContainerLight.toArgb(),
- ComposeColors.surfaceContainerDark.toArgb(),
- ),
- )
- super.onCreate(savedInstanceState)
-
- setContent {
- KeyMapperTheme {
- CreateKeyMapShortcutScreen(
- viewModel = viewModel,
- finishActivity = { finish() },
- )
- }
- }
-
- requestPermissionDelegate = RequestPermissionDelegate(this, showDialogs = true)
-
- launchRepeatOnLifecycle(Lifecycle.State.STARTED) {
- ServiceLocator.permissionAdapter(this@CreateKeyMapShortcutActivity).request
- .collectLatest { permission ->
- requestPermissionDelegate.requestPermission(
- permission,
- findNavController(R.id.container),
- )
- }
- }
-
- launchRepeatOnLifecycle(Lifecycle.State.STARTED) {
- viewModel.returnIntentResult.collectLatest { intent ->
- setResult(RESULT_OK, intent)
- finish()
- }
- }
- }
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/keymaps/CreateKeyMapShortcutUseCase.kt b/app/src/main/java/io/github/sds100/keymapper/keymaps/CreateKeyMapShortcutUseCase.kt
deleted file mode 100644
index 6d6fb7e9ed..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/keymaps/CreateKeyMapShortcutUseCase.kt
+++ /dev/null
@@ -1,86 +0,0 @@
-package io.github.sds100.keymapper.keymaps
-
-import android.content.Intent
-import android.graphics.drawable.Drawable
-import androidx.core.os.bundleOf
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.api.Api
-import io.github.sds100.keymapper.system.apps.AppShortcutAdapter
-import io.github.sds100.keymapper.util.Result
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-
-/**
- * Created by sds100 on 23/03/2021.
- */
-
-class CreateKeyMapShortcutUseCaseImpl(
- private val adapter: AppShortcutAdapter,
- resourceProvider: ResourceProvider,
-) : CreateKeyMapShortcutUseCase,
- ResourceProvider by resourceProvider {
-
- override val isSupported: Boolean
- get() = adapter.areLauncherShortcutsSupported
-
- override fun pinShortcut(
- keyMapUid: String,
- shortcutLabel: String,
- icon: Drawable?,
- ): Result<*> {
- val shortcut = if (icon == null) {
- adapter.createLauncherShortcut(
- iconResId = R.mipmap.ic_launcher_round,
- label = shortcutLabel,
- intentAction = Api.ACTION_TRIGGER_KEYMAP_BY_UID,
- bundleOf(Api.EXTRA_KEYMAP_UID to keyMapUid),
- )
- } else {
- adapter.createLauncherShortcut(
- icon = icon,
- label = shortcutLabel,
- intentAction = Api.ACTION_TRIGGER_KEYMAP_BY_UID,
- bundleOf(Api.EXTRA_KEYMAP_UID to keyMapUid),
- )
- }
- return adapter.pinShortcut(shortcut)
- }
-
- override fun createIntent(
- keyMapUid: String,
- shortcutLabel: String,
- icon: Drawable?,
- ): Intent {
- val shortcut = if (icon == null) {
- adapter.createLauncherShortcut(
- iconResId = R.mipmap.ic_launcher_round,
- label = shortcutLabel,
- intentAction = Api.ACTION_TRIGGER_KEYMAP_BY_UID,
- bundleOf(Api.EXTRA_KEYMAP_UID to keyMapUid),
- )
- } else {
- adapter.createLauncherShortcut(
- icon = icon,
- label = shortcutLabel,
- intentAction = Api.ACTION_TRIGGER_KEYMAP_BY_UID,
- bundleOf(Api.EXTRA_KEYMAP_UID to keyMapUid),
- )
- }
- return adapter.createShortcutResultIntent(shortcut)
- }
-}
-
-interface CreateKeyMapShortcutUseCase {
- val isSupported: Boolean
-
- fun pinShortcut(
- keyMapUid: String,
- shortcutLabel: String,
- icon: Drawable?,
- ): Result<*>
-
- fun createIntent(
- keyMapUid: String,
- shortcutLabel: String,
- icon: Drawable?,
- ): Intent
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/keymaps/KeyMapGroup.kt b/app/src/main/java/io/github/sds100/keymapper/keymaps/KeyMapGroup.kt
deleted file mode 100644
index 32a4a17028..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/keymaps/KeyMapGroup.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package io.github.sds100.keymapper.keymaps
-
-import io.github.sds100.keymapper.groups.Group
-import io.github.sds100.keymapper.util.State
-
-data class KeyMapGroup(
- val group: Group?,
- val subGroups: List,
- val parents: List,
- val keyMaps: State>,
-)
diff --git a/app/src/main/java/io/github/sds100/keymapper/keymaps/ShortcutModel.kt b/app/src/main/java/io/github/sds100/keymapper/keymaps/ShortcutModel.kt
deleted file mode 100644
index b55b82446d..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/keymaps/ShortcutModel.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package io.github.sds100.keymapper.keymaps
-
-import io.github.sds100.keymapper.util.ui.compose.ComposeIconInfo
-
-data class ShortcutModel(
- val icon: ComposeIconInfo,
- val text: String,
- val data: T,
-)
diff --git a/app/src/main/java/io/github/sds100/keymapper/keymaps/detection/DetectKeyMapModel.kt b/app/src/main/java/io/github/sds100/keymapper/keymaps/detection/DetectKeyMapModel.kt
deleted file mode 100644
index 7023705558..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/keymaps/detection/DetectKeyMapModel.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package io.github.sds100.keymapper.keymaps.detection
-
-import io.github.sds100.keymapper.constraints.ConstraintState
-import io.github.sds100.keymapper.keymaps.KeyMap
-
-data class DetectKeyMapModel(
- val keyMap: KeyMap,
- val groupConstraintStates: List = emptyList(),
-)
diff --git a/app/src/main/java/io/github/sds100/keymapper/logging/LogSeverity.kt b/app/src/main/java/io/github/sds100/keymapper/logging/LogSeverity.kt
deleted file mode 100644
index 225fcf2e00..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/logging/LogSeverity.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package io.github.sds100.keymapper.logging
-
-/**
- * Created by sds100 on 13/05/2021.
- */
-enum class LogSeverity {
- ERROR,
- INFO,
- DEBUG,
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/purchasing/PurchasingManager.kt b/app/src/main/java/io/github/sds100/keymapper/purchasing/PurchasingManager.kt
deleted file mode 100644
index 7ad6b4c003..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/purchasing/PurchasingManager.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package io.github.sds100.keymapper.purchasing
-
-import io.github.sds100.keymapper.util.Result
-import io.github.sds100.keymapper.util.State
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
-
-interface PurchasingManager {
- val onCompleteProductPurchase: MutableSharedFlow
- val purchases: Flow>>>
- suspend fun launchPurchasingFlow(product: ProductId): Result
- suspend fun getProductPrice(product: ProductId): Result
- suspend fun isPurchased(product: ProductId): Result
- fun refresh()
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/purchasing/PurchasingManagerImpl.kt b/app/src/main/java/io/github/sds100/keymapper/purchasing/PurchasingManagerImpl.kt
new file mode 100644
index 0000000000..0011982ba9
--- /dev/null
+++ b/app/src/main/java/io/github/sds100/keymapper/purchasing/PurchasingManagerImpl.kt
@@ -0,0 +1,30 @@
+package io.github.sds100.keymapper.purchasing
+
+import io.github.sds100.keymapper.base.purchasing.ProductId
+import io.github.sds100.keymapper.base.purchasing.PurchasingError
+import io.github.sds100.keymapper.base.purchasing.PurchasingManager
+import io.github.sds100.keymapper.common.utils.KMResult
+import io.github.sds100.keymapper.common.utils.State
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class PurchasingManagerImpl : PurchasingManager {
+ override val onCompleteProductPurchase: MutableSharedFlow = MutableSharedFlow()
+ override val purchases: Flow>>> =
+ MutableStateFlow(State.Data(PurchasingError.PurchasingNotImplemented))
+
+ override suspend fun launchPurchasingFlow(product: ProductId): KMResult {
+ return PurchasingError.PurchasingNotImplemented
+ }
+
+ override suspend fun getProductPrice(product: ProductId): KMResult {
+ return PurchasingError.PurchasingNotImplemented
+ }
+
+ override suspend fun isPurchased(product: ProductId): KMResult {
+ return PurchasingError.PurchasingNotImplemented
+ }
+
+ override fun refresh() {}
+}
diff --git a/app/src/main/java/io/github/sds100/keymapper/system/BuildUtils.kt b/app/src/main/java/io/github/sds100/keymapper/system/BuildUtils.kt
deleted file mode 100644
index 6ab2e09f90..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/system/BuildUtils.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-package io.github.sds100.keymapper.system
-
-import android.os.Build
-import android.os.Build.VERSION_CODES.JELLY_BEAN
-import android.os.Build.VERSION_CODES.JELLY_BEAN_MR1
-import android.os.Build.VERSION_CODES.JELLY_BEAN_MR2
-import android.os.Build.VERSION_CODES.KITKAT
-import android.os.Build.VERSION_CODES.LOLLIPOP
-import android.os.Build.VERSION_CODES.LOLLIPOP_MR1
-import android.os.Build.VERSION_CODES.M
-import android.os.Build.VERSION_CODES.N
-import android.os.Build.VERSION_CODES.N_MR1
-import android.os.Build.VERSION_CODES.O
-import android.os.Build.VERSION_CODES.O_MR1
-import android.os.Build.VERSION_CODES.P
-import android.os.Build.VERSION_CODES.Q
-import android.os.Build.VERSION_CODES.R
-import android.os.Build.VERSION_CODES.S
-import android.os.Build.VERSION_CODES.S_V2
-import android.os.Build.VERSION_CODES.TIRAMISU
-import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
-
-/**
- * Created by sds100 on 12/01/2019.
- */
-
-object BuildUtils {
- /**
- * @return the name of the android version for each [Build.VERSION_CODES] after the minimum api for this app.
- *
- * E.g 28 = "Pie 9.0"
- */
- fun getSdkVersionName(version: Int): String = when (version) {
- JELLY_BEAN -> "Jelly Bean 4.1"
- JELLY_BEAN_MR1 -> "Jelly Bean 4.2"
- JELLY_BEAN_MR2 -> "Jelly Bean 4.3"
- KITKAT -> "KitKat 4.4"
- LOLLIPOP -> "Lollipop 5.0"
- LOLLIPOP_MR1 -> "Lollipop 5.1"
- M -> "Marshmallow 6.0"
- N -> "Nougat 7.0"
- N_MR1 -> "Nougat 7.1"
- O -> "Oreo 8.0"
- O_MR1 -> "Oreo 8.1"
- P -> "Pie 9.0"
- Q -> "10"
- R -> "11"
- S -> "12"
- S_V2 -> "12L"
- TIRAMISU -> "13"
- UPSIDE_DOWN_CAKE -> "14"
- else -> "API $version"
- }
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/system/accessibility/AccessibilityNodeAction.kt b/app/src/main/java/io/github/sds100/keymapper/system/accessibility/AccessibilityNodeAction.kt
deleted file mode 100644
index 12af5f515c..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/system/accessibility/AccessibilityNodeAction.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package io.github.sds100.keymapper.system.accessibility
-
-/**
- * Created by sds100 on 24/04/2021.
- */
-data class AccessibilityNodeAction(val action: Int, val extras: Map = emptyMap())
diff --git a/app/src/main/java/io/github/sds100/keymapper/system/accessibility/AccessibilityServiceController.kt b/app/src/main/java/io/github/sds100/keymapper/system/accessibility/AccessibilityServiceController.kt
new file mode 100644
index 0000000000..6bc72f0bbe
--- /dev/null
+++ b/app/src/main/java/io/github/sds100/keymapper/system/accessibility/AccessibilityServiceController.kt
@@ -0,0 +1,48 @@
+package io.github.sds100.keymapper.system.accessibility
+
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import io.github.sds100.keymapper.base.actions.PerformActionsUseCaseImpl
+import io.github.sds100.keymapper.base.constraints.DetectConstraintsUseCaseImpl
+import io.github.sds100.keymapper.base.keymaps.FingerprintGesturesSupportedUseCase
+import io.github.sds100.keymapper.base.keymaps.PauseKeyMapsUseCase
+import io.github.sds100.keymapper.base.keymaps.detection.DetectKeyMapsUseCaseImpl
+import io.github.sds100.keymapper.base.reroutekeyevents.RerouteKeyEventsController
+import io.github.sds100.keymapper.base.system.accessibility.AccessibilityNodeRecorder
+import io.github.sds100.keymapper.base.system.accessibility.BaseAccessibilityServiceController
+import io.github.sds100.keymapper.data.repositories.PreferenceRepository
+import io.github.sds100.keymapper.system.devices.DevicesAdapter
+import io.github.sds100.keymapper.system.root.SuAdapter
+
+class AccessibilityServiceController @AssistedInject constructor(
+ @Assisted
+ private val service: MyAccessibilityService,
+ rerouteKeyEventsControllerFactory: RerouteKeyEventsController.Factory,
+ accessibilityNodeRecorderFactory: AccessibilityNodeRecorder.Factory,
+ performActionsUseCaseFactory: PerformActionsUseCaseImpl.Factory,
+ detectKeyMapsUseCaseFactory: DetectKeyMapsUseCaseImpl.Factory,
+ detectConstraintsUseCaseFactory: DetectConstraintsUseCaseImpl.Factory,
+ fingerprintGesturesSupported: FingerprintGesturesSupportedUseCase,
+ pauseKeyMapsUseCase: PauseKeyMapsUseCase,
+ devicesAdapter: DevicesAdapter,
+ suAdapter: SuAdapter,
+ settingsRepository: PreferenceRepository,
+) : BaseAccessibilityServiceController(
+ service = service,
+ rerouteKeyEventsControllerFactory = rerouteKeyEventsControllerFactory,
+ accessibilityNodeRecorderFactory = accessibilityNodeRecorderFactory,
+ performActionsUseCaseFactory = performActionsUseCaseFactory,
+ detectKeyMapsUseCaseFactory = detectKeyMapsUseCaseFactory,
+ detectConstraintsUseCaseFactory = detectConstraintsUseCaseFactory,
+ fingerprintGesturesSupported = fingerprintGesturesSupported,
+ pauseKeyMapsUseCase = pauseKeyMapsUseCase,
+ devicesAdapter = devicesAdapter,
+ suAdapter = suAdapter,
+ settingsRepository = settingsRepository,
+) {
+ @AssistedFactory
+ interface Factory {
+ fun create(service: MyAccessibilityService): AccessibilityServiceController
+ }
+}
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 0610083100..6d470eb0a7 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,247 +1,33 @@
package io.github.sds100.keymapper.system.accessibility
-import android.accessibilityservice.AccessibilityService
-import android.accessibilityservice.FingerprintGestureController
-import android.accessibilityservice.GestureDescription
-import android.accessibilityservice.GestureDescription.StrokeDescription
-import android.annotation.SuppressLint
-import android.app.ActivityManager
import android.content.Intent
-import android.content.res.Configuration
-import android.graphics.Path
-import android.graphics.Point
-import android.os.Build
-import android.view.KeyEvent
-import android.view.MotionEvent
-import android.view.accessibility.AccessibilityEvent
-import androidx.core.content.getSystemService
-import androidx.core.os.bundleOf
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.LifecycleRegistry
-import androidx.savedstate.SavedStateRegistry
-import androidx.savedstate.SavedStateRegistryController
-import androidx.savedstate.SavedStateRegistryOwner
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.actions.pinchscreen.PinchScreenType
-import io.github.sds100.keymapper.api.IKeyEventRelayServiceCallback
-import io.github.sds100.keymapper.api.KeyEventRelayService
-import io.github.sds100.keymapper.api.KeyEventRelayServiceWrapperImpl
-import io.github.sds100.keymapper.keymaps.FingerprintGestureType
-import io.github.sds100.keymapper.system.devices.InputDeviceUtils
-import io.github.sds100.keymapper.system.inputevents.MyKeyEvent
-import io.github.sds100.keymapper.system.inputevents.MyMotionEvent
-import io.github.sds100.keymapper.trigger.KeyEventDetectionSource
-import io.github.sds100.keymapper.util.Error
-import io.github.sds100.keymapper.util.Inject
-import io.github.sds100.keymapper.util.InputEventType
-import io.github.sds100.keymapper.util.MathUtils
-import io.github.sds100.keymapper.util.Result
-import io.github.sds100.keymapper.util.Success
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.update
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.sds100.keymapper.base.system.accessibility.BaseAccessibilityService
+import io.github.sds100.keymapper.base.system.accessibility.BaseAccessibilityServiceController
import timber.log.Timber
+import javax.inject.Inject
-/**
- * Created by sds100 on 05/04/2020.
- */
+@AndroidEntryPoint
+class MyAccessibilityService : BaseAccessibilityService() {
-class MyAccessibilityService :
- AccessibilityService(),
- LifecycleOwner,
- IAccessibilityService,
- SavedStateRegistryOwner {
+ @Inject
+ lateinit var controllerFactory: AccessibilityServiceController.Factory
- // virtual distance between fingers on multitouch gestures
- private val fingerGestureDistance = 10L
+ private var controller: AccessibilityServiceController? = null
- private var lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
- private var savedStateRegistryController: SavedStateRegistryController? =
- SavedStateRegistryController.create(this)
- override val savedStateRegistry: SavedStateRegistry
- get() = savedStateRegistryController!!.savedStateRegistry
-
- private var fingerprintGestureCallback: FingerprintGestureController.FingerprintGestureCallback? =
- null
-
- override val rootNode: AccessibilityNodeModel?
- get() {
- return rootInActiveWindow?.toModel()
- }
-
- private val _activeWindowPackage: MutableStateFlow = MutableStateFlow(null)
- override val activeWindowPackage: Flow = _activeWindowPackage
-
- override val isFingerprintGestureDetectionAvailable: Boolean
- get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- fingerprintGestureController.isGestureDetectionAvailable
- } else {
- false
- }
-
- private val _isKeyboardHidden by lazy {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- MutableStateFlow(softKeyboardController.showMode == SHOW_MODE_HIDDEN)
- } else {
- MutableStateFlow(false)
- }
- }
-
- override val isKeyboardHidden: Flow
- get() = _isKeyboardHidden
-
- override var serviceFlags: Int?
- get() = serviceInfo?.flags
- set(value) {
- if (serviceInfo != null && value != null) {
- serviceInfo = serviceInfo.apply {
- flags = value
- }
- }
- }
-
- override var serviceFeedbackType: Int?
- get() = serviceInfo?.feedbackType
- set(value) {
- if (serviceInfo != null && value != null) {
- serviceInfo = serviceInfo.apply {
- feedbackType = value
- }
- }
- }
-
- override var serviceEventTypes: Int?
- get() = serviceInfo?.eventTypes
- set(value) {
- if (serviceInfo != null && value != null) {
- serviceInfo = serviceInfo.apply {
- eventTypes = value
- }
- }
- }
-
- override var notificationTimeout: Long?
- get() = serviceInfo?.notificationTimeout
- set(value) {
- if (serviceInfo != null && value != null) {
- serviceInfo = serviceInfo.apply {
- notificationTimeout = value
- }
- }
- }
-
- private val relayServiceCallback: IKeyEventRelayServiceCallback =
- object : IKeyEventRelayServiceCallback.Stub() {
- override fun onKeyEvent(event: KeyEvent?): Boolean {
- event ?: return false
-
- val device = event.device?.let { InputDeviceUtils.createInputDeviceInfo(it) }
-
- if (controller != null) {
- return controller!!.onKeyEventFromIme(
- MyKeyEvent(
- keyCode = event.keyCode,
- action = event.action,
- metaState = event.metaState,
- scanCode = event.scanCode,
- device = device,
- repeatCount = event.repeatCount,
- source = event.source,
- ),
- )
- }
-
- return false
- }
-
- override fun onMotionEvent(event: MotionEvent?): Boolean {
- event ?: return false
-
- if (controller != null) {
- return controller!!.onMotionEventFromIme(MyMotionEvent.fromMotionEvent(event))
- }
-
- return false
- }
- }
-
- private val keyEventRelayServiceWrapper: KeyEventRelayServiceWrapperImpl by lazy {
- KeyEventRelayServiceWrapperImpl(
- ctx = this,
- id = KeyEventRelayService.CALLBACK_ID_ACCESSIBILITY_SERVICE,
- callback = relayServiceCallback,
- )
- }
-
- var controller: AccessibilityServiceController? = null
-
- override fun onCreate() {
- super.onCreate()
- Timber.i("Accessibility service: onCreate")
-
- savedStateRegistryController?.performAttach()
- savedStateRegistryController?.performRestore(null)
-
- lifecycleRegistry.currentState = Lifecycle.State.CREATED
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- softKeyboardController.addOnShowModeChangedListener { _, showMode ->
- when (showMode) {
- SHOW_MODE_AUTO -> _isKeyboardHidden.value = false
- SHOW_MODE_HIDDEN -> _isKeyboardHidden.value = true
- }
- }
- }
-
- keyEventRelayServiceWrapper.onCreate()
+ override fun getController(): BaseAccessibilityServiceController? {
+ return controller
}
override fun onServiceConnected() {
super.onServiceConnected()
- Timber.i("Accessibility service: onServiceConnected")
- lifecycleRegistry.currentState = Lifecycle.State.STARTED
-
- setTheme(R.style.AppTheme)
-
- _activeWindowPackage.update { rootInActiveWindow?.packageName?.toString() }
/*
I would put this in onCreate but for some reason on some devices getting the application
context would return null
*/
if (controller == null) {
- controller = Inject.accessibilityServiceController(this, keyEventRelayServiceWrapper)
- }
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- fingerprintGestureCallback =
- object : FingerprintGestureController.FingerprintGestureCallback() {
- override fun onGestureDetected(gesture: Int) {
- super.onGestureDetected(gesture)
-
- val id: FingerprintGestureType = when (gesture) {
- FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_DOWN ->
- FingerprintGestureType.SWIPE_DOWN
-
- FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_UP ->
- FingerprintGestureType.SWIPE_UP
-
- FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_LEFT ->
- FingerprintGestureType.SWIPE_LEFT
-
- FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_RIGHT ->
- FingerprintGestureType.SWIPE_RIGHT
-
- else -> return
- }
- controller?.onFingerprintGesture(id)
- }
- }
-
- fingerprintGestureCallback?.let {
- fingerprintGestureController.registerFingerprintGestureCallback(it, null)
- }
+ controller = controllerFactory.create(this)
}
controller?.onServiceConnected()
@@ -258,327 +44,6 @@ class MyAccessibilityService :
controller?.onDestroy()
controller = null
- lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- fingerprintGestureController
- .unregisterFingerprintGestureCallback(fingerprintGestureCallback)
- }
-
- keyEventRelayServiceWrapper.onDestroy()
-
- Timber.i("Accessibility service: onDestroy")
-
super.onDestroy()
}
-
- override fun onConfigurationChanged(newConfig: Configuration) {
- super.onConfigurationChanged(newConfig)
-
- controller?.onConfigurationChanged(newConfig)
- }
-
- override fun onTrimMemory(level: Int) {
- val memoryInfo = ActivityManager.MemoryInfo()
- getSystemService()?.getMemoryInfo(memoryInfo)
-
- Timber.i("Accessibility service: onLowMemory, total: ${memoryInfo.totalMem}, available: ${memoryInfo.availMem}, is low memory: ${memoryInfo.lowMemory}, threshold: ${memoryInfo.threshold}")
-
- super.onTrimMemory(level)
- }
-
- override fun onAccessibilityEvent(event: AccessibilityEvent?) {
- event ?: return
-
- if (event.eventType == AccessibilityEvent.TYPE_WINDOWS_CHANGED) {
- _activeWindowPackage.update { rootInActiveWindow?.packageName?.toString() }
- }
-
- controller?.onAccessibilityEvent(event)
- }
-
- override fun onKeyEvent(event: KeyEvent?): Boolean {
- event ?: return super.onKeyEvent(event)
-
- val device = if (event.device == null) {
- null
- } else {
- InputDeviceUtils.createInputDeviceInfo(event.device)
- }
-
- if (controller != null) {
- return controller!!.onKeyEvent(
- MyKeyEvent(
- keyCode = event.keyCode,
- action = event.action,
- metaState = event.metaState,
- scanCode = event.scanCode,
- device = device,
- repeatCount = event.repeatCount,
- source = event.source,
- ),
- KeyEventDetectionSource.ACCESSIBILITY_SERVICE,
- )
- }
-
- return false
- }
-
- override val lifecycle: Lifecycle
- get() = lifecycleRegistry
-
- override fun hideKeyboard() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- softKeyboardController.showMode = SHOW_MODE_HIDDEN
- }
- }
-
- override fun showKeyboard() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- softKeyboardController.showMode = SHOW_MODE_AUTO
- }
- }
-
- override fun switchIme(imeId: String) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- softKeyboardController.switchToInputMethod(imeId)
- }
- }
-
- override fun performActionOnNode(
- findNode: (node: AccessibilityNodeModel) -> Boolean,
- performAction: (node: AccessibilityNodeModel) -> AccessibilityNodeAction?,
- ): Result<*> {
- val node = rootInActiveWindow.findNodeRecursively {
- findNode(it.toModel())
- }
-
- if (node == null) {
- return Error.FailedToFindAccessibilityNode
- }
-
- val (action, extras) = performAction(node.toModel()) ?: return Success(Unit)
-
- node.performAction(action, bundleOf(*extras.toList().toTypedArray()))
- node.recycle()
-
- return Success(Unit)
- }
-
- override fun doGlobalAction(action: Int): Result<*> {
- val success = performGlobalAction(action)
-
- if (success) {
- return Success(Unit)
- } else {
- return Error.FailedToPerformAccessibilityGlobalAction(action)
- }
- }
-
- override fun tapScreen(x: Int, y: Int, inputEventType: InputEventType): Result<*> {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- val duration = 1L // ms
-
- val path = Path().apply {
- moveTo(x.toFloat(), y.toFloat())
- }
-
- val strokeDescription =
- when {
- inputEventType == InputEventType.DOWN && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ->
- StrokeDescription(
- path,
- 0,
- duration,
- true,
- )
-
- inputEventType == InputEventType.UP && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ->
- StrokeDescription(
- path,
- 59999,
- duration,
- false,
- )
-
- else -> StrokeDescription(path, 0, duration)
- }
-
- strokeDescription.let {
- val gestureDescription = GestureDescription.Builder().apply {
- addStroke(it)
- }.build()
-
- val success = dispatchGesture(gestureDescription, null, null)
-
- return if (success) {
- Success(Unit)
- } else {
- Error.FailedToDispatchGesture
- }
- }
- }
-
- return Error.SdkVersionTooLow(Build.VERSION_CODES.N)
- }
-
- override fun swipeScreen(
- xStart: Int,
- yStart: Int,
- xEnd: Int,
- yEnd: Int,
- fingerCount: Int,
- duration: Int,
- inputEventType: InputEventType,
- ): Result<*> {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- if (fingerCount >= GestureDescription.getMaxStrokeCount()) {
- return Error.GestureStrokeCountTooHigh
- }
- if (duration >= GestureDescription.getMaxGestureDuration()) {
- return Error.GestureDurationTooHigh
- }
-
- val pStart = Point(xStart, yStart)
- val pEnd = Point(xEnd, yEnd)
-
- val gestureBuilder = GestureDescription.Builder()
-
- if (fingerCount == 1) {
- val p = Path()
- p.moveTo(pStart.x.toFloat(), pStart.y.toFloat())
- p.lineTo(pEnd.x.toFloat(), pEnd.y.toFloat())
- gestureBuilder.addStroke(StrokeDescription(p, 0, duration.toLong()))
- } else {
- // segments between fingers
- val segmentCount = fingerCount - 1
- // the line of the perpendicular line which will be created to place the virtual fingers on it
- val perpendicularLineLength = (fingerGestureDistance * fingerCount).toInt()
-
- // the length of each segment between fingers
- val segmentLength = perpendicularLineLength / segmentCount
- // perpendicular line of the start swipe point
- val perpendicularLineStart = MathUtils.getPerpendicularOfLine(
- pStart,
- pEnd,
- perpendicularLineLength,
- )
- // perpendicular line of the end swipe point
- val perpendicularLineEnd = MathUtils.getPerpendicularOfLine(
- pEnd,
- pStart,
- perpendicularLineLength,
- true,
- )
-
- // this is the angle between start and end point to rotate all virtual fingers on the perpendicular lines in the same direction
- val angle =
- MathUtils.angleBetweenPoints(Point(xStart, yStart), Point(xEnd, yEnd)) - 90
-
- // create the virtual fingers
- for (index in 0..segmentCount) {
- // offset of each finger
- val fingerOffsetLength = index * segmentLength * 2
- // move the coordinates of the current virtual finger on the perpendicular line for the start coordinates
- val startFingerCoordinateWithOffset =
- MathUtils.movePointByDistanceAndAngle(
- perpendicularLineStart.start,
- fingerOffsetLength,
- angle,
- )
- // move the coordinates of the current virtual finger on the perpendicular line for the end coordinates
- val endFingerCoordinateWithOffset =
- MathUtils.movePointByDistanceAndAngle(
- perpendicularLineEnd.start,
- fingerOffsetLength,
- angle,
- )
-
- // create a path for each finger, move the the coordinates on the perpendicular line and draw it to the end coordinates of the perpendicular line of the end swipe point
- val p = Path()
- p.moveTo(
- startFingerCoordinateWithOffset.x.toFloat(),
- startFingerCoordinateWithOffset.y.toFloat(),
- )
- p.lineTo(
- endFingerCoordinateWithOffset.x.toFloat(),
- endFingerCoordinateWithOffset.y.toFloat(),
- )
-
- gestureBuilder.addStroke(StrokeDescription(p, 0, duration.toLong()))
- }
- }
-
- val success = dispatchGesture(gestureBuilder.build(), null, null)
-
- return if (success) {
- Success(Unit)
- } else {
- Error.FailedToDispatchGesture
- }
- }
-
- return Error.SdkVersionTooLow(Build.VERSION_CODES.N)
- }
-
- override fun pinchScreen(
- x: Int,
- y: Int,
- distance: Int,
- pinchType: PinchScreenType,
- fingerCount: Int,
- duration: Int,
- inputEventType: InputEventType,
- ): Result<*> {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- if (fingerCount >= GestureDescription.getMaxStrokeCount()) {
- return Error.GestureStrokeCountTooHigh
- }
- if (duration >= GestureDescription.getMaxGestureDuration()) {
- return Error.GestureDurationTooHigh
- }
-
- val gestureBuilder = GestureDescription.Builder()
- val distributedPoints: List =
- MathUtils.distributePointsOnCircle(Point(x, y), distance.toFloat() / 2, fingerCount)
-
- for (index in distributedPoints.indices) {
- val p = Path()
- if (pinchType == PinchScreenType.PINCH_IN) {
- p.moveTo(x.toFloat(), y.toFloat())
- p.lineTo(
- distributedPoints[index].x.toFloat(),
- distributedPoints[index].y.toFloat(),
- )
- } else {
- p.moveTo(
- distributedPoints[index].x.toFloat(),
- distributedPoints[index].y.toFloat(),
- )
- p.lineTo(x.toFloat(), y.toFloat())
- }
-
- gestureBuilder.addStroke(StrokeDescription(p, 0, duration.toLong()))
- }
-
- val success = dispatchGesture(gestureBuilder.build(), null, null)
-
- return if (success) {
- Success(Unit)
- } else {
- Error.FailedToDispatchGesture
- }
- }
-
- return Error.SdkVersionTooLow(Build.VERSION_CODES.N)
- }
-
- override fun findFocussedNode(focus: Int): AccessibilityNodeModel? = findFocus(focus)?.toModel()
-
- override fun setInputMethodEnabled(imeId: String, enabled: Boolean) {
- @SuppressLint("CheckResult")
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- softKeyboardController.setInputMethodEnabled(imeId, enabled)
- }
- }
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/system/airplanemode/AirplaneModeAdapter.kt b/app/src/main/java/io/github/sds100/keymapper/system/airplanemode/AirplaneModeAdapter.kt
deleted file mode 100644
index f4ce8d93b6..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/system/airplanemode/AirplaneModeAdapter.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.github.sds100.keymapper.system.airplanemode
-
-import io.github.sds100.keymapper.util.Result
-
-/**
- * Created by sds100 on 24/04/2021.
- */
-interface AirplaneModeAdapter {
- fun isEnabled(): Boolean
- fun enable(): Result<*>
- fun disable(): Result<*>
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/system/apps/DisplayAppsUseCase.kt b/app/src/main/java/io/github/sds100/keymapper/system/apps/DisplayAppsUseCase.kt
deleted file mode 100644
index 22abf49a84..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/system/apps/DisplayAppsUseCase.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-package io.github.sds100.keymapper.system.apps
-
-import android.graphics.drawable.Drawable
-import io.github.sds100.keymapper.util.Result
-import io.github.sds100.keymapper.util.State
-import kotlinx.coroutines.flow.Flow
-
-/**
- * Created by sds100 on 04/04/2021.
- */
-
-class DisplayAppsUseCaseImpl(
- private val adapter: PackageManagerAdapter,
-) : DisplayAppsUseCase {
- override val installedPackages: Flow>> = adapter.installedPackages
-
- override fun getAppName(packageName: String): Result = adapter.getAppName(packageName)
-
- override fun getAppIcon(packageName: String): Result = adapter.getAppIcon(packageName)
-
- override fun getActivityLabel(packageName: String, activityClass: String): Result =
- adapter.getActivityLabel(packageName, activityClass)
-
- override fun getActivityIcon(packageName: String, activityClass: String): Result =
- adapter.getActivityIcon(packageName, activityClass)
-}
-
-interface DisplayAppsUseCase {
- val installedPackages: Flow>>
-
- fun getActivityLabel(packageName: String, activityClass: String): Result
- fun getActivityIcon(packageName: String, activityClass: String): Result
- fun getAppName(packageName: String): Result
- fun getAppIcon(packageName: String): Result
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/system/camera/CameraLensUtils.kt b/app/src/main/java/io/github/sds100/keymapper/system/camera/CameraLensUtils.kt
deleted file mode 100644
index 80f7090f2d..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/system/camera/CameraLensUtils.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package io.github.sds100.keymapper.system.camera
-
-import io.github.sds100.keymapper.R
-
-/**
- * Created by sds100 on 23/03/2021.
- */
-object CameraLensUtils {
- fun getLabel(lens: CameraLens) = when (lens) {
- CameraLens.FRONT -> R.string.lens_front
- CameraLens.BACK -> R.string.lens_back
- }
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/system/display/OrientationUtils.kt b/app/src/main/java/io/github/sds100/keymapper/system/display/OrientationUtils.kt
deleted file mode 100644
index fc3607627e..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/system/display/OrientationUtils.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package io.github.sds100.keymapper.system.display
-
-import io.github.sds100.keymapper.R
-
-/**
- * Created by sds100 on 23/03/2021.
- */
-object OrientationUtils {
- fun getLabel(orientation: Orientation) = when (orientation) {
- Orientation.ORIENTATION_0 -> R.string.orientation_0
- Orientation.ORIENTATION_90 -> R.string.orientation_90
- Orientation.ORIENTATION_180 -> R.string.orientation_180
- Orientation.ORIENTATION_270 -> R.string.orientation_270
- }
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/system/inputmethod/ShowHideInputMethodUseCase.kt b/app/src/main/java/io/github/sds100/keymapper/system/inputmethod/ShowHideInputMethodUseCase.kt
deleted file mode 100644
index feb12d0e2a..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/system/inputmethod/ShowHideInputMethodUseCase.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package io.github.sds100.keymapper.system.inputmethod
-
-import io.github.sds100.keymapper.system.accessibility.ServiceAdapter
-import io.github.sds100.keymapper.util.ServiceEvent
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.mapNotNull
-import kotlinx.coroutines.runBlocking
-
-/**
- * Created by sds100 on 16/04/2021.
- */
-
-class ShowHideInputMethodUseCaseImpl(
- private val serviceAdapter: ServiceAdapter,
-) : ShowHideInputMethodUseCase {
- override val onHiddenChange: Flow = serviceAdapter.eventReceiver.mapNotNull {
- when (it) {
- ServiceEvent.OnHideKeyboardEvent -> true
- ServiceEvent.OnShowKeyboardEvent -> false
- else -> null
- }
- }
-
- override fun show() {
- runBlocking { serviceAdapter.send(ServiceEvent.ShowKeyboard) }
- }
-
- override fun hide() {
- runBlocking { serviceAdapter.send(ServiceEvent.HideKeyboard) }
- }
-}
-
-interface ShowHideInputMethodUseCase {
- val onHiddenChange: Flow
- fun show()
- fun hide()
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/system/inputmethod/ToggleCompatibleImeUseCase.kt b/app/src/main/java/io/github/sds100/keymapper/system/inputmethod/ToggleCompatibleImeUseCase.kt
deleted file mode 100644
index 86e559f13e..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/system/inputmethod/ToggleCompatibleImeUseCase.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package io.github.sds100.keymapper.system.inputmethod
-
-import io.github.sds100.keymapper.util.Result
-import kotlinx.coroutines.flow.Flow
-
-/**
- * Created by sds100 on 16/04/2021.
- */
-
-class ToggleCompatibleImeUseCaseImpl(
- private val inputMethodAdapter: InputMethodAdapter,
-) : ToggleCompatibleImeUseCase {
- private val keyMapperImeHelper = KeyMapperImeHelper(inputMethodAdapter)
-
- override val sufficientPermissions: Flow =
- inputMethodAdapter.isUserInputRequiredToChangeIme
-
- override suspend fun toggle(): Result =
- keyMapperImeHelper.toggleCompatibleInputMethod()
-}
-
-interface ToggleCompatibleImeUseCase {
- val sufficientPermissions: Flow
-
- suspend fun toggle(): Result
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/system/nfc/NfcAdapter.kt b/app/src/main/java/io/github/sds100/keymapper/system/nfc/NfcAdapter.kt
deleted file mode 100644
index 68e0286fff..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/system/nfc/NfcAdapter.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.github.sds100.keymapper.system.nfc
-
-import io.github.sds100.keymapper.util.Result
-
-/**
- * Created by sds100 on 24/04/2021.
- */
-interface NfcAdapter {
- fun isEnabled(): Boolean
- fun enable(): Result<*>
- fun disable(): Result<*>
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/system/popup/AndroidToastAdapter.kt b/app/src/main/java/io/github/sds100/keymapper/system/popup/AndroidToastAdapter.kt
deleted file mode 100644
index 633fdb5424..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/system/popup/AndroidToastAdapter.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package io.github.sds100.keymapper.system.popup
-
-import android.content.Context
-import splitties.toast.toast
-
-/**
- * Created by sds100 on 17/04/2021.
- */
-class AndroidToastAdapter(context: Context) : PopupMessageAdapter {
- private val ctx: Context = context.applicationContext
-
- override fun showPopupMessage(message: String) {
- ctx.toast(message)
- }
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/system/popup/PopupMessageAdapter.kt b/app/src/main/java/io/github/sds100/keymapper/system/popup/PopupMessageAdapter.kt
deleted file mode 100644
index 6f0fc56638..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/system/popup/PopupMessageAdapter.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package io.github.sds100.keymapper.system.popup
-
-/**
- * Created by sds100 on 17/04/2021.
- */
-interface PopupMessageAdapter {
- fun showPopupMessage(message: String)
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/system/shell/ShellAdapter.kt b/app/src/main/java/io/github/sds100/keymapper/system/shell/ShellAdapter.kt
deleted file mode 100644
index 553bc744bd..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/system/shell/ShellAdapter.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package io.github.sds100.keymapper.system.shell
-
-import io.github.sds100.keymapper.util.Result
-
-/**
- * Created by sds100 on 21/04/2021.
- */
-
-interface ShellAdapter {
- fun execute(command: String): Result<*>
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/system/url/AndroidOpenUrlAdapter.kt b/app/src/main/java/io/github/sds100/keymapper/system/url/AndroidOpenUrlAdapter.kt
deleted file mode 100644
index 6109d32e98..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/system/url/AndroidOpenUrlAdapter.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package io.github.sds100.keymapper.system.url
-
-import android.content.Context
-import io.github.sds100.keymapper.util.Result
-
-/**
- * Created by sds100 on 24/04/2021.
- */
-class AndroidOpenUrlAdapter(context: Context) : OpenUrlAdapter {
-
- private val ctx = context.applicationContext
-
- override fun openUrl(url: String): Result<*> = UrlUtils.openUrl(ctx, url)
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/system/url/OpenUrlAdapter.kt b/app/src/main/java/io/github/sds100/keymapper/system/url/OpenUrlAdapter.kt
deleted file mode 100644
index a6c01f428d..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/system/url/OpenUrlAdapter.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package io.github.sds100.keymapper.system.url
-
-import io.github.sds100.keymapper.util.Result
-
-/**
- * Created by sds100 on 24/04/2021.
- */
-interface OpenUrlAdapter {
- fun openUrl(url: String): Result<*>
-}
diff --git a/app/src/free/java/io/github/sds100/keymapper/trigger/AdvancedTriggersBottomSheet.kt b/app/src/main/java/io/github/sds100/keymapper/trigger/AdvancedTriggersBottomSheet.kt
similarity index 98%
rename from app/src/free/java/io/github/sds100/keymapper/trigger/AdvancedTriggersBottomSheet.kt
rename to app/src/main/java/io/github/sds100/keymapper/trigger/AdvancedTriggersBottomSheet.kt
index dc1abbe1d9..cc85192142 100644
--- a/app/src/free/java/io/github/sds100/keymapper/trigger/AdvancedTriggersBottomSheet.kt
+++ b/app/src/main/java/io/github/sds100/keymapper/trigger/AdvancedTriggersBottomSheet.kt
@@ -29,8 +29,8 @@ import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.compose.KeyMapperTheme
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.compose.KeyMapperTheme
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
diff --git a/app/src/main/java/io/github/sds100/keymapper/trigger/ConfigTriggerViewModel.kt b/app/src/main/java/io/github/sds100/keymapper/trigger/ConfigTriggerViewModel.kt
new file mode 100644
index 0000000000..1654023097
--- /dev/null
+++ b/app/src/main/java/io/github/sds100/keymapper/trigger/ConfigTriggerViewModel.kt
@@ -0,0 +1,48 @@
+package io.github.sds100.keymapper.trigger
+
+import io.github.sds100.keymapper.base.keymaps.ConfigKeyMapUseCase
+import io.github.sds100.keymapper.base.keymaps.CreateKeyMapShortcutUseCase
+import io.github.sds100.keymapper.base.keymaps.DisplayKeyMapUseCase
+import io.github.sds100.keymapper.base.keymaps.FingerprintGesturesSupportedUseCase
+import io.github.sds100.keymapper.base.onboarding.OnboardingUseCase
+import io.github.sds100.keymapper.base.purchasing.PurchasingManager
+import io.github.sds100.keymapper.base.trigger.BaseConfigTriggerViewModel
+import io.github.sds100.keymapper.base.trigger.RecordTriggerUseCase
+import io.github.sds100.keymapper.base.trigger.SetupGuiKeyboardUseCase
+import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider
+import io.github.sds100.keymapper.base.utils.ui.DialogProvider
+import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
+import kotlinx.coroutines.CoroutineScope
+import javax.inject.Inject
+
+class ConfigTriggerViewModel @Inject constructor(
+ private val coroutineScope: CoroutineScope,
+ private val onboarding: OnboardingUseCase,
+ private val config: ConfigKeyMapUseCase,
+ private val recordTrigger: RecordTriggerUseCase,
+ private val createKeyMapShortcut: CreateKeyMapShortcutUseCase,
+ private val displayKeyMap: DisplayKeyMapUseCase,
+ private val purchasingManager: PurchasingManager,
+ private val setupGuiKeyboard: SetupGuiKeyboardUseCase,
+ private val fingerprintGesturesSupported: FingerprintGesturesSupportedUseCase,
+ resourceProvider: ResourceProvider,
+ navigationProvider: NavigationProvider,
+ dialogProvider: DialogProvider,
+) : BaseConfigTriggerViewModel(
+ coroutineScope = coroutineScope,
+ onboarding = onboarding,
+ config = config,
+ recordTrigger = recordTrigger,
+ createKeyMapShortcut = createKeyMapShortcut,
+ displayKeyMap = displayKeyMap,
+ purchasingManager = purchasingManager,
+ setupGuiKeyboard = setupGuiKeyboard,
+ fingerprintGesturesSupported = fingerprintGesturesSupported,
+ resourceProvider = resourceProvider,
+ navigationProvider = navigationProvider,
+ dialogProvider = dialogProvider,
+) {
+ override fun onEditFloatingButtonClick() {}
+
+ override fun onEditFloatingLayoutClick() {}
+}
diff --git a/app/src/main/java/io/github/sds100/keymapper/trigger/TriggerScreen.kt b/app/src/main/java/io/github/sds100/keymapper/trigger/TriggerScreen.kt
index edbf72bd54..6c1da6cf77 100644
--- a/app/src/main/java/io/github/sds100/keymapper/trigger/TriggerScreen.kt
+++ b/app/src/main/java/io/github/sds100/keymapper/trigger/TriggerScreen.kt
@@ -1,63 +1,16 @@
package io.github.sds100.keymapper.trigger
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
-import androidx.compose.foundation.layout.widthIn
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.itemsIndexed
-import androidx.compose.foundation.lazy.rememberLazyListState
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.rounded.Fingerprint
-import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.material3.Text
-import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.tooling.preview.Devices
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import androidx.window.core.layout.WindowHeightSizeClass
-import androidx.window.core.layout.WindowWidthSizeClass
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.compose.KeyMapperTheme
-import io.github.sds100.keymapper.keymaps.ClickType
-import io.github.sds100.keymapper.keymaps.ShortcutModel
-import io.github.sds100.keymapper.keymaps.ShortcutRow
-import io.github.sds100.keymapper.util.State
-import io.github.sds100.keymapper.util.ui.LinkType
-import io.github.sds100.keymapper.util.ui.compose.ComposeIconInfo
-import io.github.sds100.keymapper.util.ui.compose.DraggableItem
-import io.github.sds100.keymapper.util.ui.compose.RadioButtonText
-import io.github.sds100.keymapper.util.ui.compose.rememberDragDropState
+import io.github.sds100.keymapper.base.trigger.BaseTriggerScreen
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TriggerScreen(modifier: Modifier = Modifier, viewModel: ConfigTriggerViewModel) {
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
- val setupGuiKeyboardState by viewModel.setupGuiKeyboardState.collectAsStateWithLifecycle()
- val recordTriggerState by viewModel.recordTriggerState.collectAsStateWithLifecycle()
-
- HandleAssistantTriggerSetupBottomSheet(viewModel = viewModel)
if (viewModel.showAdvancedTriggersBottomSheet) {
AdvancedTriggersBottomSheet(
@@ -70,643 +23,5 @@ fun TriggerScreen(modifier: Modifier = Modifier, viewModel: ConfigTriggerViewMod
)
}
- if (viewModel.showDpadTriggerSetupBottomSheet) {
- DpadTriggerSetupBottomSheet(
- modifier = Modifier.systemBarsPadding(),
- onDismissRequest = {
- viewModel.showDpadTriggerSetupBottomSheet = false
- },
- guiKeyboardState = setupGuiKeyboardState,
- onEnableKeyboardClick = viewModel::onEnableGuiKeyboardClick,
- onChooseKeyboardClick = viewModel::onChooseGuiKeyboardClick,
- onNeverShowAgainClick = viewModel::onNeverShowSetupDpadClick,
- sheetState = sheetState,
- )
- }
-
- if (viewModel.showNoKeysRecordedBottomSheet) {
- NoKeysRecordedBottomSheet(
- modifier = Modifier.systemBarsPadding(),
- onDismissRequest = {
- viewModel.showNoKeysRecordedBottomSheet = false
- },
- viewModel = viewModel,
- sheetState = sheetState,
- )
- }
-
- val triggerKeyOptionsState by viewModel.triggerKeyOptionsState.collectAsStateWithLifecycle()
-
- if (triggerKeyOptionsState != null) {
- TriggerKeyOptionsBottomSheet(
- modifier = Modifier.systemBarsPadding(),
- sheetState = sheetState,
- state = triggerKeyOptionsState!!,
- onDismissRequest = viewModel::onDismissTriggerKeyOptions,
- onCheckDoNotRemap = viewModel::onCheckDoNotRemap,
- onSelectClickType = viewModel::onSelectKeyClickType,
- onSelectDevice = viewModel::onSelectTriggerKeyDevice,
- onSelectAssistantType = viewModel::onSelectTriggerKeyAssistantType,
- onEditFloatingButtonClick = viewModel::onEditFloatingButtonClick,
- onEditFloatingLayoutClick = viewModel::onEditFloatingLayoutClick,
- onSelectFingerprintGestureType = viewModel::onSelectFingerprintGestureType,
- )
- }
-
- val configState by viewModel.state.collectAsStateWithLifecycle()
-
- when (val state = configState) {
- is State.Loading -> Loading(modifier = modifier)
- is State.Data -> {
- if (isHorizontalLayout()) {
- TriggerScreenHorizontal(
- modifier = modifier,
- configState = state.data,
- recordTriggerState = recordTriggerState,
- onRemoveClick = viewModel::onRemoveKeyClick,
- onEditClick = viewModel::onTriggerKeyOptionsClick,
- onRecordTriggerClick = viewModel::onRecordTriggerButtonClick,
- onAdvancedTriggersClick = viewModel::onAdvancedTriggersClick,
- onSelectClickType = viewModel::onClickTypeRadioButtonChecked,
- onSelectParallelMode = viewModel::onParallelRadioButtonChecked,
- onSelectSequenceMode = viewModel::onSequenceRadioButtonChecked,
- onMoveTriggerKey = viewModel::onMoveTriggerKey,
- onFixErrorClick = viewModel::onTriggerErrorClick,
- onClickShortcut = viewModel::onClickTriggerKeyShortcut,
- onRecordTriggerTapTargetCompleted = viewModel::onRecordTriggerTapTargetCompleted,
- onSkipTapTarget = viewModel::onSkipTapTargetClick,
- onAdvancedTriggerTapTargetCompleted = viewModel::onAdvancedTriggersTapTargetCompleted,
- )
- } else {
- TriggerScreenVertical(
- modifier = modifier,
- configState = state.data,
- recordTriggerState = recordTriggerState,
- onRemoveClick = viewModel::onRemoveKeyClick,
- onEditClick = viewModel::onTriggerKeyOptionsClick,
- onRecordTriggerClick = viewModel::onRecordTriggerButtonClick,
- onAdvancedTriggersClick = viewModel::onAdvancedTriggersClick,
- onSelectClickType = viewModel::onClickTypeRadioButtonChecked,
- onSelectParallelMode = viewModel::onParallelRadioButtonChecked,
- onSelectSequenceMode = viewModel::onSequenceRadioButtonChecked,
- onMoveTriggerKey = viewModel::onMoveTriggerKey,
- onFixErrorClick = viewModel::onTriggerErrorClick,
- onClickShortcut = viewModel::onClickTriggerKeyShortcut,
- onRecordTriggerTapTargetCompleted = viewModel::onRecordTriggerTapTargetCompleted,
- onSkipTapTarget = viewModel::onSkipTapTargetClick,
- onAdvancedTriggerTapTargetCompleted = viewModel::onAdvancedTriggersTapTargetCompleted,
- )
- }
- }
- }
-}
-
-@Composable
-private fun isHorizontalLayout(): Boolean {
- val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
-
- return windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT
-}
-
-@Composable
-private fun isVerticalCompactLayout(): Boolean {
- val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
-
- return windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT && windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT
-}
-
-@Composable
-private fun Loading(modifier: Modifier = Modifier) {
- Box(modifier = modifier, contentAlignment = Alignment.Center) {
- CircularProgressIndicator()
- }
-}
-
-@Composable
-private fun TriggerScreenVertical(
- modifier: Modifier = Modifier,
- configState: ConfigTriggerState,
- recordTriggerState: RecordTriggerState,
- onRemoveClick: (String) -> Unit = {},
- onEditClick: (String) -> Unit = {},
- onSelectClickType: (ClickType) -> Unit = {},
- onSelectParallelMode: () -> Unit = {},
- onSelectSequenceMode: () -> Unit = {},
- onRecordTriggerClick: () -> Unit = {},
- onAdvancedTriggersClick: () -> Unit = {},
- onMoveTriggerKey: (fromIndex: Int, toIndex: Int) -> Unit = { _, _ -> },
- onFixErrorClick: (TriggerError) -> Unit = {},
- onClickShortcut: (TriggerKeyShortcut) -> Unit = {},
- onRecordTriggerTapTargetCompleted: () -> Unit = {},
- onSkipTapTarget: () -> Unit = {},
- onAdvancedTriggerTapTargetCompleted: () -> Unit = {},
-) {
- Surface(modifier = modifier) {
- Column {
- when (configState) {
- is ConfigTriggerState.Empty -> {
- Column(
- modifier = Modifier
- .weight(1f)
- .verticalScroll(state = rememberScrollState()),
- verticalArrangement = Arrangement.Center,
- ) {
- Text(
- modifier = Modifier.padding(32.dp),
- text = stringResource(R.string.triggers_recyclerview_placeholder),
- textAlign = TextAlign.Center,
- )
-
- if (configState.shortcuts.isNotEmpty()) {
- Column(horizontalAlignment = Alignment.CenterHorizontally) {
- Text(
- text = stringResource(R.string.trigger_shortcuts_header),
- style = MaterialTheme.typography.titleSmall,
- )
-
- Spacer(Modifier.height(8.dp))
-
- ShortcutRow(
- modifier = Modifier
- .padding(horizontal = 32.dp)
- .fillMaxWidth(),
- shortcuts = configState.shortcuts,
- onClick = onClickShortcut,
- )
- }
- }
- }
- }
-
- is ConfigTriggerState.Loaded -> {
- val isCompact = isVerticalCompactLayout()
- Spacer(Modifier.height(8.dp))
-
- TriggerList(
- modifier = Modifier.weight(1f),
- triggerList = configState.triggerKeys,
- shortcuts = configState.shortcuts,
- isReorderingEnabled = configState.isReorderingEnabled,
- onEditClick = onEditClick,
- onRemoveClick = onRemoveClick,
- onMove = onMoveTriggerKey,
- onClickShortcut = onClickShortcut,
- onFixErrorClick = onFixErrorClick,
- )
-
- if (configState.clickTypeButtons.isNotEmpty()) {
- ClickTypeRadioGroup(
- modifier = Modifier.padding(horizontal = 8.dp),
- clickTypes = configState.clickTypeButtons,
- checkedClickType = configState.checkedClickType,
- onSelectClickType = onSelectClickType,
- maxLines = if (isCompact) 1 else 2,
- )
- }
-
- if (configState.triggerModeButtonsVisible) {
- if (!isCompact) {
- Text(
- modifier = Modifier.padding(horizontal = 8.dp),
- text = stringResource(R.string.press_dot_dot_dot),
- style = MaterialTheme.typography.labelLarge,
- )
- }
-
- TriggerModeRadioGroup(
- modifier = Modifier.padding(horizontal = 8.dp),
- mode = configState.checkedTriggerMode,
- isEnabled = configState.triggerModeButtonsEnabled,
- onSelectParallelMode = onSelectParallelMode,
- onSelectSequenceMode = onSelectSequenceMode,
- maxLines = if (isCompact) 1 else 2,
- )
- }
- }
- }
-
- RecordTriggerButtonRow(
- modifier = Modifier
- .fillMaxWidth()
- .padding(start = 8.dp, end = 8.dp, bottom = 8.dp),
- onRecordTriggerClick = onRecordTriggerClick,
- recordTriggerState = recordTriggerState,
- onAdvancedTriggersClick = onAdvancedTriggersClick,
- showRecordTriggerTapTarget = (configState as? ConfigTriggerState.Empty)?.showRecordTriggerTapTarget
- ?: false,
- onRecordTriggerTapTargetCompleted = onRecordTriggerTapTargetCompleted,
- onSkipTapTarget = onSkipTapTarget,
- showAdvancedTriggerTapTarget = configState.showAdvancedTriggersTapTarget,
- onAdvancedTriggerTapTargetCompleted = onAdvancedTriggerTapTargetCompleted,
- )
- }
- }
-}
-
-@Composable
-private fun TriggerScreenHorizontal(
- modifier: Modifier = Modifier,
- configState: ConfigTriggerState,
- recordTriggerState: RecordTriggerState,
- onRemoveClick: (String) -> Unit = {},
- onEditClick: (String) -> Unit = {},
- onSelectClickType: (ClickType) -> Unit = {},
- onSelectParallelMode: () -> Unit = {},
- onSelectSequenceMode: () -> Unit = {},
- onRecordTriggerClick: () -> Unit = {},
- onAdvancedTriggersClick: () -> Unit = {},
- onMoveTriggerKey: (fromIndex: Int, toIndex: Int) -> Unit = { _, _ -> },
- onFixErrorClick: (TriggerError) -> Unit = {},
- onClickShortcut: (TriggerKeyShortcut) -> Unit = {},
- onRecordTriggerTapTargetCompleted: () -> Unit = {},
- onSkipTapTarget: () -> Unit = {},
- onAdvancedTriggerTapTargetCompleted: () -> Unit = {},
-) {
- Surface(modifier = modifier) {
- when (configState) {
- is ConfigTriggerState.Empty -> Row {
- Text(
- modifier = Modifier
- .widthIn(max = 400.dp)
- .padding(32.dp)
- .verticalScroll(state = rememberScrollState()),
- text = stringResource(R.string.triggers_recyclerview_placeholder),
- textAlign = TextAlign.Center,
- )
- Column {
- if (configState.shortcuts.isNotEmpty()) {
- Column(
- modifier = Modifier
- .weight(1f)
- .verticalScroll(state = rememberScrollState()),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center,
- ) {
- Text(
- text = stringResource(R.string.trigger_shortcuts_header),
- style = MaterialTheme.typography.titleSmall,
- )
-
- Spacer(Modifier.height(8.dp))
-
- ShortcutRow(
- modifier = Modifier
- .padding(horizontal = 32.dp)
- .fillMaxWidth(),
- shortcuts = configState.shortcuts,
- onClick = onClickShortcut,
- )
- }
- }
-
- RecordTriggerButtonRow(
- modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp),
- onRecordTriggerClick = onRecordTriggerClick,
- recordTriggerState = recordTriggerState,
- onAdvancedTriggersClick = onAdvancedTriggersClick,
- showRecordTriggerTapTarget = (configState as? ConfigTriggerState.Empty)?.showRecordTriggerTapTarget
- ?: false,
- onRecordTriggerTapTargetCompleted = onRecordTriggerTapTargetCompleted,
- onSkipTapTarget = onSkipTapTarget,
- showAdvancedTriggerTapTarget = configState.showAdvancedTriggersTapTarget,
- )
- }
- }
-
- is ConfigTriggerState.Loaded -> Row {
- TriggerList(
- modifier = Modifier
- .fillMaxHeight()
- .widthIn(max = 400.dp),
- triggerList = configState.triggerKeys,
- shortcuts = configState.shortcuts,
- isReorderingEnabled = configState.isReorderingEnabled,
- onEditClick = onEditClick,
- onRemoveClick = onRemoveClick,
- onMove = onMoveTriggerKey,
- onClickShortcut = onClickShortcut,
- onFixErrorClick = onFixErrorClick,
- )
-
- Spacer(Modifier.height(8.dp))
-
- Column(
- modifier = Modifier.fillMaxHeight(),
- verticalArrangement = Arrangement.Bottom,
- ) {
- Column(
- modifier = Modifier
- .weight(1f)
- .verticalScroll(rememberScrollState()),
- ) {
- if (configState.clickTypeButtons.isNotEmpty()) {
- ClickTypeRadioGroup(
- modifier = Modifier.padding(horizontal = 8.dp),
- clickTypes = configState.clickTypeButtons,
- checkedClickType = configState.checkedClickType,
- onSelectClickType = onSelectClickType,
- )
- }
-
- Text(
- modifier = Modifier.padding(horizontal = 8.dp),
- text = stringResource(R.string.press_dot_dot_dot),
- style = MaterialTheme.typography.labelLarge,
- )
-
- if (configState.triggerModeButtonsVisible) {
- TriggerModeRadioGroup(
- modifier = Modifier.padding(horizontal = 8.dp),
- mode = configState.checkedTriggerMode,
- isEnabled = configState.triggerModeButtonsEnabled,
- onSelectParallelMode = onSelectParallelMode,
- onSelectSequenceMode = onSelectSequenceMode,
- )
- }
- }
-
- RecordTriggerButtonRow(
- modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp),
- onRecordTriggerClick = onRecordTriggerClick,
- recordTriggerState = recordTriggerState,
- onAdvancedTriggersClick = onAdvancedTriggersClick,
- showRecordTriggerTapTarget = false,
- onRecordTriggerTapTargetCompleted = onRecordTriggerTapTargetCompleted,
- onSkipTapTarget = onSkipTapTarget,
- showAdvancedTriggerTapTarget = configState.showAdvancedTriggersTapTarget,
- onAdvancedTriggerTapTargetCompleted = onAdvancedTriggerTapTargetCompleted,
- )
- }
- }
- }
- }
-}
-
-@Composable
-private fun TriggerList(
- modifier: Modifier = Modifier,
- triggerList: List,
- shortcuts: Set>,
- isReorderingEnabled: Boolean,
- onRemoveClick: (String) -> Unit,
- onEditClick: (String) -> Unit,
- onFixErrorClick: (TriggerError) -> Unit,
- onMove: (fromIndex: Int, toIndex: Int) -> Unit,
- onClickShortcut: (TriggerKeyShortcut) -> Unit,
-) {
- val lazyListState = rememberLazyListState()
- val dragDropState = rememberDragDropState(
- lazyListState = lazyListState,
- onMove = onMove,
- // Do not drag and drop the row of shortcuts
- ignoreLastItems = if (shortcuts.isEmpty()) {
- 0
- } else {
- 1
- },
- )
-
- // Use dragContainer rather than .draggable() modifier because that causes
- // dragging the first item to be always be dropped in the next position.
- LazyColumn(
- modifier = modifier,
- state = lazyListState,
- contentPadding = PaddingValues(vertical = 8.dp),
- ) {
- itemsIndexed(
- triggerList,
- key = { _, item -> item.id },
- contentType = { _, _ -> "key" },
- ) { index, model ->
- DraggableItem(
- dragDropState = dragDropState,
- index = index,
- ) { isDragging ->
- TriggerKeyListItem(
- modifier = Modifier.fillMaxWidth(),
- model = model,
- index = index,
- isDraggingEnabled = triggerList.size > 1,
- isDragging = isDragging,
- isReorderingEnabled = isReorderingEnabled,
- dragDropState = dragDropState,
- onEditClick = { onEditClick(model.id) },
- onRemoveClick = { onRemoveClick(model.id) },
- onFixClick = onFixErrorClick,
- )
- }
- }
-
- if (shortcuts.isNotEmpty()) {
- item(key = "shortcuts", contentType = "shortcuts") {
- Column(horizontalAlignment = Alignment.CenterHorizontally) {
- Text(
- text = stringResource(R.string.trigger_shortcuts_header),
- style = MaterialTheme.typography.titleSmall,
- )
-
- Spacer(Modifier.height(8.dp))
-
- ShortcutRow(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 32.dp),
- shortcuts = shortcuts,
- onClick = { onClickShortcut(it) },
- )
- }
- }
- }
- }
-}
-
-@Composable
-private fun ClickTypeRadioGroup(
- modifier: Modifier = Modifier,
- clickTypes: Set,
- checkedClickType: ClickType?,
- onSelectClickType: (ClickType) -> Unit,
- maxLines: Int = 2,
-) {
- Column(modifier = modifier) {
- Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
- if (clickTypes.contains(ClickType.SHORT_PRESS)) {
- RadioButtonText(
- modifier = Modifier.weight(1f),
- isSelected = checkedClickType == ClickType.SHORT_PRESS,
- text = stringResource(R.string.radio_button_short_press),
- onSelected = { onSelectClickType(ClickType.SHORT_PRESS) },
- maxLines = maxLines,
- )
- }
- if (clickTypes.contains(ClickType.LONG_PRESS)) {
- RadioButtonText(
- modifier = Modifier.weight(1f),
- isSelected = checkedClickType == ClickType.LONG_PRESS,
- text = stringResource(R.string.radio_button_long_press),
- onSelected = { onSelectClickType(ClickType.LONG_PRESS) },
- maxLines = maxLines,
- )
- }
- if (clickTypes.contains(ClickType.DOUBLE_PRESS)) {
- RadioButtonText(
- modifier = Modifier.weight(1f),
- isSelected = checkedClickType == ClickType.DOUBLE_PRESS,
- text = stringResource(R.string.radio_button_double_press),
- onSelected = { onSelectClickType(ClickType.DOUBLE_PRESS) },
- maxLines = maxLines,
- )
- }
- }
- }
-}
-
-@Composable
-private fun TriggerModeRadioGroup(
- modifier: Modifier = Modifier,
- mode: TriggerMode,
- isEnabled: Boolean,
- onSelectParallelMode: () -> Unit,
- onSelectSequenceMode: () -> Unit,
- maxLines: Int = 2,
-) {
- Column(modifier = modifier) {
- Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
- RadioButtonText(
- modifier = Modifier.weight(1f),
- isSelected = mode is TriggerMode.Parallel,
- isEnabled = isEnabled,
- text = stringResource(R.string.radio_button_parallel),
- onSelected = onSelectParallelMode,
- maxLines = maxLines,
- )
- RadioButtonText(
- modifier = Modifier.weight(1f),
- isSelected = mode == TriggerMode.Sequence,
- isEnabled = isEnabled,
- text = stringResource(R.string.radio_button_sequence),
- onSelected = onSelectSequenceMode,
- maxLines = maxLines,
- )
- }
- }
-}
-
-private val sampleList = listOf(
- TriggerKeyListItemModel.KeyCode(
- id = "id1",
- keyName = "Volume Up",
- clickType = ClickType.SHORT_PRESS,
- extraInfo = "External Keyboard",
- linkType = LinkType.ARROW,
- error = null,
- ),
- TriggerKeyListItemModel.FloatingButton(
- id = "id2",
- buttonName = "😎",
- layoutName = "Gaming",
- clickType = ClickType.DOUBLE_PRESS,
- linkType = LinkType.ARROW,
- error = null,
- ),
- TriggerKeyListItemModel.Assistant(
- id = "id3",
- assistantType = AssistantTriggerType.DEVICE,
- clickType = ClickType.DOUBLE_PRESS,
- linkType = LinkType.HIDDEN,
- error = null,
- ),
-)
-
-private val previewState = ConfigTriggerState.Loaded(
- triggerKeys = sampleList,
- isReorderingEnabled = true,
- clickTypeButtons = setOf(
- ClickType.SHORT_PRESS,
- ClickType.LONG_PRESS,
- ClickType.DOUBLE_PRESS,
- ),
- checkedClickType = ClickType.LONG_PRESS,
- checkedTriggerMode = TriggerMode.Sequence,
- triggerModeButtonsEnabled = true,
- triggerModeButtonsVisible = true,
- shortcuts = setOf(
- ShortcutModel(
- icon = ComposeIconInfo.Vector(Icons.Rounded.Fingerprint),
- text = "Fingerprint gesture",
- data = TriggerKeyShortcut.FINGERPRINT_GESTURE,
- ),
- ),
-)
-
-@Preview(device = Devices.PIXEL)
-@Composable
-private fun VerticalPreview() {
- KeyMapperTheme {
- TriggerScreenVertical(
- configState = previewState,
- recordTriggerState = RecordTriggerState.Idle,
- )
- }
-}
-
-@Preview(heightDp = 400, widthDp = 300)
-@Composable
-private fun VerticalPreviewTiny() {
- KeyMapperTheme {
- TriggerScreenVertical(
- configState = previewState,
- recordTriggerState = RecordTriggerState.Idle,
- )
- }
-}
-
-@Preview(device = Devices.PIXEL)
-@Composable
-private fun VerticalEmptyPreview() {
- KeyMapperTheme {
- TriggerScreenVertical(
- configState = ConfigTriggerState.Empty(
- shortcuts = setOf(
- ShortcutModel(
- icon = ComposeIconInfo.Vector(Icons.Rounded.Fingerprint),
- text = "Fingerprint gesture",
- data = TriggerKeyShortcut.FINGERPRINT_GESTURE,
- ),
- ),
- ),
- recordTriggerState = RecordTriggerState.Idle,
- )
- }
-}
-
-@Preview(widthDp = 800, heightDp = 300)
-@Composable
-private fun HorizontalPreview() {
- KeyMapperTheme {
- TriggerScreenHorizontal(
- configState = previewState,
- recordTriggerState = RecordTriggerState.Idle,
- )
- }
-}
-
-@Preview(widthDp = 800, heightDp = 300)
-@Composable
-private fun HorizontalEmptyPreview() {
- KeyMapperTheme {
- TriggerScreenHorizontal(
- configState = ConfigTriggerState.Empty(
- shortcuts = setOf(
- ShortcutModel(
- icon = ComposeIconInfo.Vector(Icons.Rounded.Fingerprint),
- text = "Fingerprint gesture",
- data = TriggerKeyShortcut.FINGERPRINT_GESTURE,
- ),
- ),
-
- ),
- recordTriggerState = RecordTriggerState.Idle,
- )
- }
+ BaseTriggerScreen(modifier, viewModel)
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/util/ErrorUtils.kt b/app/src/main/java/io/github/sds100/keymapper/util/ErrorUtils.kt
deleted file mode 100644
index 8a047e25b0..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/util/ErrorUtils.kt
+++ /dev/null
@@ -1,184 +0,0 @@
-package io.github.sds100.keymapper.util
-
-import android.content.pm.PackageManager
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.purchasing.ProductId
-import io.github.sds100.keymapper.system.BuildUtils
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-
-/**
- * Created by sds100 on 29/02/2020.
- */
-
-fun Error.getFullMessage(resourceProvider: ResourceProvider): String = when (this) {
- is Error.PermissionDenied ->
- Error.PermissionDenied.getMessageForPermission(
- resourceProvider,
- permission,
- )
-
- is Error.AppNotFound -> resourceProvider.getString(
- R.string.error_app_isnt_installed,
- packageName,
- )
-
- is Error.AppDisabled -> resourceProvider.getString(
- R.string.error_app_is_disabled_package_name,
- this.packageName,
- )
-
- is Error.NoCompatibleImeEnabled -> resourceProvider.getString(R.string.error_key_mapper_ime_service_disabled)
- is Error.NoCompatibleImeChosen -> resourceProvider.getString(R.string.error_ime_must_be_chosen)
- is Error.SystemFeatureNotSupported -> when (this.feature) {
- PackageManager.FEATURE_NFC -> resourceProvider.getString(R.string.error_system_feature_nfc_unsupported)
- PackageManager.FEATURE_CAMERA -> resourceProvider.getString(R.string.error_system_feature_camera_unsupported)
- PackageManager.FEATURE_FINGERPRINT -> resourceProvider.getString(R.string.error_system_feature_fingerprint_unsupported)
- PackageManager.FEATURE_WIFI -> resourceProvider.getString(R.string.error_system_feature_wifi_unsupported)
- PackageManager.FEATURE_BLUETOOTH -> resourceProvider.getString(R.string.error_system_feature_bluetooth_unsupported)
- PackageManager.FEATURE_DEVICE_ADMIN -> resourceProvider.getString(R.string.error_system_feature_device_admin_unsupported)
- PackageManager.FEATURE_CAMERA_FLASH -> resourceProvider.getString(R.string.error_system_feature_camera_flash_unsupported)
- PackageManager.FEATURE_TELEPHONY -> resourceProvider.getString(R.string.error_system_feature_telephony_unsupported)
- else -> throw Exception("Don't know how to get error message for this system feature ${this.feature}")
- }
-
- is Error.ExtraNotFound -> resourceProvider.getString(R.string.error_extra_not_found, extraId)
- is Error.SdkVersionTooLow -> resourceProvider.getString(
- R.string.error_sdk_version_too_low,
- BuildUtils.getSdkVersionName(minSdk),
- )
-
- is Error.SdkVersionTooHigh -> resourceProvider.getString(
- R.string.error_sdk_version_too_high,
- BuildUtils.getSdkVersionName(maxSdk),
- )
-
- is Error.InputMethodNotFound -> resourceProvider.getString(
- R.string.error_ime_not_found,
- imeLabel,
- )
-
- is Error.FrontFlashNotFound -> resourceProvider.getString(R.string.error_front_flash_not_found)
- is Error.BackFlashNotFound -> resourceProvider.getString(R.string.error_back_flash_not_found)
- is Error.DeviceNotFound -> resourceProvider.getString(R.string.error_device_not_found)
- is Error.Exception -> exception.toString()
- is Error.EmptyJson -> resourceProvider.getString(R.string.error_empty_json)
- is Error.InvalidNumber -> resourceProvider.getString(R.string.error_invalid_number)
- is Error.NumberTooSmall -> resourceProvider.getString(R.string.error_number_too_small, min)
- is Error.NumberTooBig -> resourceProvider.getString(R.string.error_number_too_big, max)
- is Error.EmptyText -> resourceProvider.getString(R.string.error_cant_be_empty)
- Error.BackupVersionTooNew -> resourceProvider.getString(R.string.error_backup_version_too_new)
- Error.NoIncompatibleKeyboardsInstalled -> resourceProvider.getString(R.string.error_no_incompatible_input_methods_installed)
- Error.NoMediaSessions -> resourceProvider.getString(R.string.error_no_media_sessions)
- Error.NoVoiceAssistant -> resourceProvider.getString(R.string.error_voice_assistant_not_found)
- Error.AccessibilityServiceDisabled -> resourceProvider.getString(R.string.error_accessibility_service_disabled)
- Error.LauncherShortcutsNotSupported -> resourceProvider.getString(R.string.error_launcher_shortcuts_not_supported)
- Error.AccessibilityServiceCrashed -> resourceProvider.getString(R.string.error_accessibility_service_crashed)
- Error.CantFindImeSettings -> resourceProvider.getString(R.string.error_cant_find_ime_settings)
- Error.CantShowImePickerInBackground -> resourceProvider.getString(R.string.error_cant_show_ime_picker_in_background)
- Error.FailedToFindAccessibilityNode -> resourceProvider.getString(R.string.error_failed_to_find_accessibility_node)
- is Error.FailedToPerformAccessibilityGlobalAction -> resourceProvider.getString(
- R.string.error_failed_to_perform_accessibility_global_action,
- action,
- )
-
- Error.FailedToDispatchGesture -> resourceProvider.getString(R.string.error_failed_to_dispatch_gesture)
- Error.AppShortcutCantBeOpened -> resourceProvider.getString(R.string.error_opening_app_shortcut)
- Error.InsufficientPermissionsToOpenAppShortcut -> resourceProvider.getString(R.string.error_keymapper_doesnt_have_permission_app_shortcut)
- Error.NoAppToPhoneCall -> resourceProvider.getString(R.string.error_no_app_to_phone_call)
-
- Error.CameraInUse -> resourceProvider.getString(R.string.error_camera_in_use)
- Error.CameraError -> resourceProvider.getString(R.string.error_camera_error)
- Error.CameraDisabled -> resourceProvider.getString(R.string.error_camera_disabled)
- Error.CameraDisconnected -> resourceProvider.getString(R.string.error_camera_disconnected)
- Error.MaxCamerasInUse -> resourceProvider.getString(R.string.error_max_cameras_in_use)
- Error.CameraVariableFlashlightStrengthUnsupported -> resourceProvider.getString(R.string.error_variable_flashlight_strength_unsupported)
-
- is Error.FailedToModifySystemSetting -> resourceProvider.getString(
- R.string.error_failed_to_modify_system_setting,
- setting,
- )
-
- is Error.ImeDisabled -> resourceProvider.getString(R.string.error_ime_disabled, this.ime.label)
- Error.FailedToChangeIme -> resourceProvider.getString(R.string.error_failed_to_change_ime)
- Error.NoCameraApp -> resourceProvider.getString(R.string.error_no_camera_app)
- Error.NoDeviceAssistant -> resourceProvider.getString(R.string.error_no_device_assistant)
- Error.NoSettingsApp -> resourceProvider.getString(R.string.error_no_settings_app)
- Error.NoAppToOpenUrl -> resourceProvider.getString(R.string.error_no_app_to_open_url)
-
- Error.CantFindSoundFile -> resourceProvider.getString(R.string.error_cant_find_sound_file)
- is Error.CorruptJsonFile -> reason
-
- is Error.CannotCreateFileInTarget -> resourceProvider.getString(
- R.string.error_file_access_denied,
- uri,
- )
-
- Error.FileOperationCancelled -> resourceProvider.getString(R.string.error_file_operation_cancelled)
- is Error.NoSpaceLeftOnTarget -> resourceProvider.getString(
- R.string.error_no_space_left_at_target,
- uri,
- )
-
- is Error.NotADirectory -> resourceProvider.getString(R.string.error_not_a_directory, uri)
- is Error.NotAFile -> resourceProvider.getString(R.string.error_not_a_file, uri)
- is Error.SourceFileNotFound -> resourceProvider.getString(
- R.string.error_source_file_not_found,
- uri,
- )
-
- Error.StoragePermissionDenied -> resourceProvider.getString(R.string.error_storage_permission_denied)
- Error.TargetDirectoryMatchesSourceDirectory -> resourceProvider.getString(R.string.error_matching_source_and_target_paths)
- is Error.TargetDirectoryNotFound -> resourceProvider.getString(
- R.string.error_directory_not_found,
- uri,
- )
-
- is Error.TargetFileNotFound -> resourceProvider.getString(
- R.string.error_target_file_not_found,
- uri,
- )
-
- Error.UnknownIOError -> resourceProvider.getString(R.string.error_io_error)
- Error.ShizukuNotStarted -> resourceProvider.getString(R.string.error_shizuku_not_started)
- Error.NoFileName -> resourceProvider.getString(R.string.error_no_file_name)
- Error.CantDetectKeyEventsInPhoneCall -> resourceProvider.getString(R.string.trigger_error_cant_detect_in_phone_call_explanation)
- Error.GestureStrokeCountTooHigh -> resourceProvider.getString(R.string.trigger_error_gesture_stroke_count_too_high)
- Error.GestureDurationTooHigh -> resourceProvider.getString(R.string.trigger_error_gesture_duration_too_high)
-
- Error.PurchasingError.Cancelled -> resourceProvider.getString(R.string.purchasing_error_cancelled)
- Error.PurchasingError.NetworkError -> resourceProvider.getString(R.string.purchasing_error_network)
- Error.PurchasingError.ProductNotFound -> resourceProvider.getString(R.string.purchasing_error_product_not_found)
- Error.PurchasingError.StoreProblem -> resourceProvider.getString(R.string.purchasing_error_store_problem)
- Error.PurchasingError.PaymentPending -> resourceProvider.getString(R.string.purchasing_error_payment_pending)
- Error.PurchasingError.PurchaseInvalid -> resourceProvider.getString(R.string.purchasing_error_purchase_invalid)
- is Error.PurchasingError.Unexpected -> this.message
-
- is Error.ProductNotPurchased -> when (this.product) {
- ProductId.ASSISTANT_TRIGGER -> resourceProvider.getString(R.string.purchasing_error_assistant_not_purchased_home_screen)
- ProductId.FLOATING_BUTTONS -> resourceProvider.getString(R.string.purchasing_error_floating_buttons_not_purchased_home_screen)
- }
-
- Error.PurchasingNotImplemented -> resourceProvider.getString(R.string.purchasing_error_not_implemented)
- Error.DpadTriggerImeNotSelected -> resourceProvider.getString(R.string.trigger_error_dpad_ime_not_selected)
- Error.InvalidBackup -> resourceProvider.getString(R.string.error_invalid_backup)
- Error.MalformedUrl -> resourceProvider.getString(R.string.error_malformed_url)
- Error.UiElementNotFound -> resourceProvider.getString(R.string.error_ui_element_not_found)
-}
-
-val Error.isFixable: Boolean
- get() = when (this) {
- is Error.AppNotFound,
- is Error.AppDisabled,
- Error.NoCompatibleImeEnabled,
- Error.NoCompatibleImeChosen,
- is Error.ImeDisabled,
- Error.AccessibilityServiceDisabled,
- Error.AccessibilityServiceCrashed,
- is Error.PermissionDenied,
- is Error.ShizukuNotStarted,
- is Error.CantDetectKeyEventsInPhoneCall,
-
- -> true
-
- else -> false
- }
diff --git a/app/src/main/java/io/github/sds100/keymapper/util/Inject.kt b/app/src/main/java/io/github/sds100/keymapper/util/Inject.kt
deleted file mode 100644
index 3ad39fcfac..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/util/Inject.kt
+++ /dev/null
@@ -1,260 +0,0 @@
-package io.github.sds100.keymapper.util
-
-import android.content.Context
-import androidx.lifecycle.lifecycleScope
-import io.github.sds100.keymapper.KeyMapperApp
-import io.github.sds100.keymapper.ServiceLocator
-import io.github.sds100.keymapper.UseCases
-import io.github.sds100.keymapper.actions.ChooseActionViewModel
-import io.github.sds100.keymapper.actions.TestActionUseCaseImpl
-import io.github.sds100.keymapper.actions.keyevent.ChooseKeyCodeViewModel
-import io.github.sds100.keymapper.actions.keyevent.ConfigKeyEventActionViewModel
-import io.github.sds100.keymapper.actions.keyevent.ConfigKeyEventUseCaseImpl
-import io.github.sds100.keymapper.actions.pinchscreen.PinchPickDisplayCoordinateViewModel
-import io.github.sds100.keymapper.actions.sound.ChooseSoundFileUseCaseImpl
-import io.github.sds100.keymapper.actions.sound.ChooseSoundFileViewModel
-import io.github.sds100.keymapper.actions.swipescreen.SwipePickDisplayCoordinateViewModel
-import io.github.sds100.keymapper.actions.tapscreen.PickDisplayCoordinateViewModel
-import io.github.sds100.keymapper.actions.uielement.InteractUiElementViewModel
-import io.github.sds100.keymapper.api.KeyEventRelayServiceWrapper
-import io.github.sds100.keymapper.backup.BackupRestoreMappingsUseCaseImpl
-import io.github.sds100.keymapper.constraints.ChooseConstraintViewModel
-import io.github.sds100.keymapper.constraints.CreateConstraintUseCaseImpl
-import io.github.sds100.keymapper.home.HomeViewModel
-import io.github.sds100.keymapper.home.ShowHomeScreenAlertsUseCaseImpl
-import io.github.sds100.keymapper.keymaps.ConfigKeyMapViewModel
-import io.github.sds100.keymapper.keymaps.CreateKeyMapShortcutViewModel
-import io.github.sds100.keymapper.keymaps.FingerprintGesturesSupportedUseCaseImpl
-import io.github.sds100.keymapper.keymaps.ListKeyMapsUseCaseImpl
-import io.github.sds100.keymapper.logging.DisplayLogUseCaseImpl
-import io.github.sds100.keymapper.logging.LogViewModel
-import io.github.sds100.keymapper.settings.ConfigSettingsUseCaseImpl
-import io.github.sds100.keymapper.settings.SettingsViewModel
-import io.github.sds100.keymapper.sorting.SortKeyMapsUseCaseImpl
-import io.github.sds100.keymapper.system.accessibility.AccessibilityServiceController
-import io.github.sds100.keymapper.system.accessibility.MyAccessibilityService
-import io.github.sds100.keymapper.system.apps.ChooseActivityViewModel
-import io.github.sds100.keymapper.system.apps.ChooseAppShortcutViewModel
-import io.github.sds100.keymapper.system.apps.ChooseAppViewModel
-import io.github.sds100.keymapper.system.apps.DisplayAppShortcutsUseCaseImpl
-import io.github.sds100.keymapper.system.bluetooth.ChooseBluetoothDeviceUseCaseImpl
-import io.github.sds100.keymapper.system.bluetooth.ChooseBluetoothDeviceViewModel
-import io.github.sds100.keymapper.system.inputmethod.ShowInputMethodPickerUseCaseImpl
-import io.github.sds100.keymapper.system.intents.ConfigIntentViewModel
-import io.github.sds100.keymapper.trigger.SetupGuiKeyboardUseCaseImpl
-
-/**
- * Created by sds100 on 26/01/2020.
- */
-
-object Inject {
-
- fun chooseActionViewModel(ctx: Context): ChooseActionViewModel.Factory = ChooseActionViewModel.Factory(
- UseCases.createAction(ctx),
- ServiceLocator.resourceProvider(ctx),
- )
-
- fun chooseAppViewModel(context: Context): ChooseAppViewModel.Factory = ChooseAppViewModel.Factory(
- UseCases.displayPackages(context),
- )
-
- fun chooseActivityViewModel(context: Context): ChooseActivityViewModel.Factory = ChooseActivityViewModel.Factory(
- UseCases.displayPackages(context),
- )
-
- fun chooseAppShortcutViewModel(context: Context): ChooseAppShortcutViewModel.Factory = ChooseAppShortcutViewModel.Factory(
- DisplayAppShortcutsUseCaseImpl(
- ServiceLocator.appShortcutAdapter(context),
- ),
- ServiceLocator.resourceProvider(context),
- )
-
- fun chooseConstraintListViewModel(ctx: Context): ChooseConstraintViewModel.Factory = ChooseConstraintViewModel.Factory(
- CreateConstraintUseCaseImpl(
- ServiceLocator.networkAdapter(ctx),
- ServiceLocator.inputMethodAdapter(ctx),
- ServiceLocator.settingsRepository(ctx),
- ServiceLocator.cameraAdapter(ctx),
- ),
- ServiceLocator.resourceProvider(ctx),
- )
-
- fun configKeyEventViewModel(
- context: Context,
- ): ConfigKeyEventActionViewModel.Factory {
- val useCase = ConfigKeyEventUseCaseImpl(
- preferenceRepository = ServiceLocator.settingsRepository(context),
- devicesAdapter = ServiceLocator.devicesAdapter(context),
- )
- return ConfigKeyEventActionViewModel.Factory(
- useCase,
- ServiceLocator.resourceProvider(context),
- )
- }
-
- fun chooseKeyCodeViewModel(): ChooseKeyCodeViewModel.Factory = ChooseKeyCodeViewModel.Factory()
-
- fun configIntentViewModel(ctx: Context): ConfigIntentViewModel.Factory = ConfigIntentViewModel.Factory(ServiceLocator.resourceProvider(ctx))
-
- fun soundFileActionTypeViewModel(ctx: Context): ChooseSoundFileViewModel.Factory = ChooseSoundFileViewModel.Factory(
- ServiceLocator.resourceProvider(ctx),
- ChooseSoundFileUseCaseImpl(
- ServiceLocator.fileAdapter(ctx),
- ServiceLocator.soundsManager(ctx),
- ),
- )
-
- fun tapCoordinateActionTypeViewModel(context: Context): PickDisplayCoordinateViewModel.Factory = PickDisplayCoordinateViewModel.Factory(
- ServiceLocator.resourceProvider(context),
- )
-
- fun swipeCoordinateActionTypeViewModel(context: Context): SwipePickDisplayCoordinateViewModel.Factory = SwipePickDisplayCoordinateViewModel.Factory(
- ServiceLocator.resourceProvider(context),
- )
-
- fun pinchCoordinateActionTypeViewModel(context: Context): PinchPickDisplayCoordinateViewModel.Factory = PinchPickDisplayCoordinateViewModel.Factory(
- ServiceLocator.resourceProvider(context),
- )
-
- fun configKeyMapViewModel(
- ctx: Context,
- ): ConfigKeyMapViewModel.Factory = ConfigKeyMapViewModel.Factory(
- UseCases.configKeyMap(ctx),
- TestActionUseCaseImpl(ServiceLocator.accessibilityServiceAdapter(ctx)),
- UseCases.onboarding(ctx),
- (ctx.applicationContext as KeyMapperApp).recordTriggerController,
- UseCases.createKeymapShortcut(ctx),
- UseCases.displayKeyMap(ctx),
- UseCases.createAction(ctx),
- ServiceLocator.resourceProvider(ctx),
- ServiceLocator.purchasingManager(ctx),
- SetupGuiKeyboardUseCaseImpl(
- ServiceLocator.inputMethodAdapter(ctx),
- ServiceLocator.packageManagerAdapter(ctx),
- ),
- FingerprintGesturesSupportedUseCaseImpl(ServiceLocator.settingsRepository(ctx)),
- )
-
- fun createActionShortcutViewModel(
- ctx: Context,
- ): CreateKeyMapShortcutViewModel.Factory = CreateKeyMapShortcutViewModel.Factory(
- UseCases.configKeyMap(ctx),
- ListKeyMapsUseCaseImpl(
- ServiceLocator.roomKeyMapRepository(ctx),
- ServiceLocator.groupRepository(ctx),
- ServiceLocator.floatingButtonRepository(ctx),
- ServiceLocator.fileAdapter(ctx),
- ServiceLocator.backupManager(ctx),
- ServiceLocator.resourceProvider(ctx),
- UseCases.displayKeyMap(ctx),
- ),
- UseCases.createKeymapShortcut(ctx),
- ServiceLocator.resourceProvider(ctx),
- )
-
- fun homeViewModel(ctx: Context): HomeViewModel.Factory = HomeViewModel.Factory(
- ListKeyMapsUseCaseImpl(
- ServiceLocator.roomKeyMapRepository(ctx),
- ServiceLocator.groupRepository(ctx),
- ServiceLocator.floatingButtonRepository(ctx),
- ServiceLocator.fileAdapter(ctx),
- ServiceLocator.backupManager(ctx),
- ServiceLocator.resourceProvider(ctx),
- UseCases.displayKeyMap(ctx),
- ),
- UseCases.pauseKeyMaps(ctx),
- BackupRestoreMappingsUseCaseImpl(
- ServiceLocator.fileAdapter(ctx),
- ServiceLocator.backupManager(ctx),
- ),
- ShowHomeScreenAlertsUseCaseImpl(
- ServiceLocator.settingsRepository(ctx),
- ServiceLocator.permissionAdapter(ctx),
- ServiceLocator.accessibilityServiceAdapter(ctx),
- UseCases.pauseKeyMaps(ctx),
- ),
- UseCases.onboarding(ctx),
- ServiceLocator.resourceProvider(ctx),
- SetupGuiKeyboardUseCaseImpl(
- ServiceLocator.inputMethodAdapter(ctx),
- ServiceLocator.packageManagerAdapter(ctx),
- ),
- SortKeyMapsUseCaseImpl(
- ServiceLocator.settingsRepository(ctx),
- UseCases.displayKeyMap(ctx),
- ),
- UseCases.listFloatingLayouts(ctx),
- ShowInputMethodPickerUseCaseImpl(ServiceLocator.inputMethodAdapter(ctx)),
- )
-
- fun settingsViewModel(context: Context): SettingsViewModel.Factory = SettingsViewModel.Factory(
- ConfigSettingsUseCaseImpl(
- ServiceLocator.settingsRepository(context),
- ServiceLocator.permissionAdapter(context),
- ServiceLocator.inputMethodAdapter(context),
- ServiceLocator.soundsManager(context),
- ServiceLocator.suAdapter(context),
- ServiceLocator.packageManagerAdapter(context),
- ServiceLocator.shizukuAdapter(context),
- ServiceLocator.devicesAdapter(context),
- ),
- ServiceLocator.resourceProvider(context),
- )
-
- fun accessibilityServiceController(
- service: MyAccessibilityService,
- keyEventRelayService: KeyEventRelayServiceWrapper,
- ): AccessibilityServiceController = AccessibilityServiceController(
- coroutineScope = service.lifecycleScope,
- accessibilityService = service,
- inputEvents = ServiceLocator.accessibilityServiceAdapter(service).eventsToService,
- outputEvents = ServiceLocator.accessibilityServiceAdapter(service).eventReceiver,
- detectConstraintsUseCase = UseCases.detectConstraints(service),
- performActionsUseCase = UseCases.performActions(
- ctx = service,
- service = service,
- keyEventRelayService = keyEventRelayService,
- ),
- detectKeyMapsUseCase = UseCases.detectKeyMaps(
- ctx = service,
- service = service,
- keyEventRelayService = keyEventRelayService,
- ),
- fingerprintGesturesSupportedUseCase = UseCases.fingerprintGesturesSupported(service),
- pauseKeyMapsUseCase = UseCases.pauseKeyMaps(service),
- devicesAdapter = ServiceLocator.devicesAdapter(service),
- suAdapter = ServiceLocator.suAdapter(service),
- rerouteKeyEventsUseCase = UseCases.rerouteKeyEvents(
- ctx = service,
- keyEventRelayService = keyEventRelayService,
- ),
- inputMethodAdapter = ServiceLocator.inputMethodAdapter(service),
- settingsRepository = ServiceLocator.settingsRepository(service),
- nodeRepository = ServiceLocator.accessibilityNodeRepository(service),
- )
-
- fun chooseBluetoothDeviceViewModel(ctx: Context): ChooseBluetoothDeviceViewModel.Factory = ChooseBluetoothDeviceViewModel.Factory(
- ChooseBluetoothDeviceUseCaseImpl(
- ServiceLocator.devicesAdapter(ctx),
- ServiceLocator.permissionAdapter(ctx),
- ),
- ServiceLocator.resourceProvider(ctx),
- )
-
- fun logViewModel(ctx: Context): LogViewModel.Factory = LogViewModel.Factory(
- DisplayLogUseCaseImpl(
- ServiceLocator.logRepository(ctx),
- ServiceLocator.resourceProvider(ctx),
- ServiceLocator.clipboardAdapter(ctx),
- ServiceLocator.fileAdapter(ctx),
- ),
- ServiceLocator.resourceProvider(ctx),
- )
-
- fun interactUiElementViewModel(
- ctx: Context,
- ): InteractUiElementViewModel.Factory = InteractUiElementViewModel.Factory(
- (ctx.applicationContext as KeyMapperApp).interactUiElementController,
- resourceProvider = ServiceLocator.resourceProvider(ctx),
- )
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/util/InputEventType.kt b/app/src/main/java/io/github/sds100/keymapper/util/InputEventType.kt
deleted file mode 100644
index fb6769cffd..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/util/InputEventType.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package io.github.sds100.keymapper.util
-
-/**
- * Created by sds100 on 28/07/20.
- */
-enum class InputEventType {
- DOWN_UP,
- DOWN,
- UP,
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/util/LoggingUtils.kt b/app/src/main/java/io/github/sds100/keymapper/util/LoggingUtils.kt
deleted file mode 100644
index b5d0f02ef0..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/util/LoggingUtils.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package io.github.sds100.keymapper.util
-
-import timber.log.Timber
-import kotlin.system.measureTimeMillis
-
-/**
- * Created by sds100 on 19/01/21.
- */
-
-inline fun logMeasureTimeMillis(block: () -> T): T {
- val result: T
- val time = measureTimeMillis {
- result = block.invoke()
- }
-
- Timber.e("$time ms")
-
- return result
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/util/Result.kt b/app/src/main/java/io/github/sds100/keymapper/util/Result.kt
deleted file mode 100644
index 8fe8251769..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/util/Result.kt
+++ /dev/null
@@ -1,225 +0,0 @@
-package io.github.sds100.keymapper.util
-
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.purchasing.ProductId
-import io.github.sds100.keymapper.system.inputmethod.ImeInfo
-import io.github.sds100.keymapper.system.permissions.Permission
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-
-/**
- * Created by sds100 on 26/02/2020.
- */
-
-/**
- * Inspired from @antonyharfield great example!
- */
-
-sealed class Result
-
-data class Success(val value: T) : Result()
-
-sealed class Error : Result() {
- data class Exception(val exception: java.lang.Exception) : Error()
- data class SystemFeatureNotSupported(val feature: String) : Error()
- data class ExtraNotFound(val extraId: String) : Error()
- data class SdkVersionTooLow(val minSdk: Int) : Error()
- data class SdkVersionTooHigh(val maxSdk: Int) : Error()
- data class InputMethodNotFound(val imeLabel: String) : Error()
- data object NoVoiceAssistant : Error()
- data object NoDeviceAssistant : Error()
- data object NoCameraApp : Error()
- data object NoSettingsApp : Error()
- data object FrontFlashNotFound : Error()
- data object BackFlashNotFound : Error()
- data class ImeDisabled(val ime: ImeInfo) : Error()
- data class DeviceNotFound(val descriptor: String) : Error()
- data object InvalidNumber : Error()
- data class NumberTooBig(val max: Int) : Error()
- data class NumberTooSmall(val min: Int) : Error()
- data object EmptyText : Error()
- data object NoIncompatibleKeyboardsInstalled : Error()
- data object NoMediaSessions : Error()
- data object BackupVersionTooNew : Error()
- data object LauncherShortcutsNotSupported : Error()
-
- data class AppNotFound(val packageName: String) : Error()
- data class AppDisabled(val packageName: String) : Error()
- data object AppShortcutCantBeOpened : Error()
- data object InsufficientPermissionsToOpenAppShortcut : Error()
- data object NoCompatibleImeEnabled : Error()
- data object NoCompatibleImeChosen : Error()
-
- data object AccessibilityServiceDisabled : Error()
- data object AccessibilityServiceCrashed : Error()
-
- data object CantShowImePickerInBackground : Error()
- data object CantFindImeSettings : Error()
- data object GestureStrokeCountTooHigh : Error()
- data object GestureDurationTooHigh : Error()
-
- data class PermissionDenied(val permission: Permission) : Error() {
- companion object {
-
- fun getMessageForPermission(
- resourceProvider: ResourceProvider,
- permission: Permission,
- ): String {
- val resId = when (permission) {
- Permission.WRITE_SETTINGS -> R.string.error_action_requires_write_settings_permission
- Permission.CAMERA -> R.string.error_action_requires_camera_permission
- Permission.DEVICE_ADMIN -> R.string.error_need_to_enable_device_admin
- Permission.READ_PHONE_STATE -> R.string.error_action_requires_read_phone_state_permission
- Permission.ACCESS_NOTIFICATION_POLICY -> R.string.error_action_notification_policy_permission
- Permission.WRITE_SECURE_SETTINGS -> R.string.error_need_write_secure_settings_permission
- Permission.NOTIFICATION_LISTENER -> R.string.error_denied_notification_listener_service_permission
- Permission.CALL_PHONE -> R.string.error_denied_call_phone_permission
- Permission.ROOT -> R.string.error_requires_root
- Permission.IGNORE_BATTERY_OPTIMISATION -> R.string.error_battery_optimisation_enabled
- Permission.SHIZUKU -> R.string.error_shizuku_permission_denied
- Permission.ACCESS_FINE_LOCATION -> R.string.error_access_fine_location_permission_denied
- Permission.ANSWER_PHONE_CALL -> R.string.error_answer_end_phone_call_permission_denied
- Permission.FIND_NEARBY_DEVICES -> R.string.error_find_nearby_devices_permission_denied
- Permission.POST_NOTIFICATIONS -> R.string.error_notifications_permission_denied
- }
-
- return resourceProvider.getString(resId)
- }
- }
- }
-
- data object FailedToFindAccessibilityNode : Error()
- data class FailedToPerformAccessibilityGlobalAction(val action: Int) : Error()
- data object FailedToDispatchGesture : Error()
-
- data object CameraInUse : Error()
- data object CameraDisconnected : Error()
- data object CameraDisabled : Error()
- data object MaxCamerasInUse : Error()
- data object CameraError : Error()
- data object CameraVariableFlashlightStrengthUnsupported : Error()
-
- data class FailedToModifySystemSetting(val setting: String) : Error()
- data object FailedToChangeIme : Error()
- data object NoAppToOpenUrl : Error()
- data object NoAppToPhoneCall : Error()
-
- data class NotAFile(val uri: String) : Error()
- data class NotADirectory(val uri: String) : Error()
- data object StoragePermissionDenied : Error()
- data class CannotCreateFileInTarget(val uri: String) : Error()
- data class SourceFileNotFound(val uri: String) : Error()
- data class TargetFileNotFound(val uri: String) : Error()
- data class TargetDirectoryNotFound(val uri: String) : Error()
- data object UnknownIOError : Error()
- data object FileOperationCancelled : Error()
- data object TargetDirectoryMatchesSourceDirectory : Error()
- data class NoSpaceLeftOnTarget(val uri: String) : Error()
- data object NoFileName : Error()
- data object InvalidBackup : Error()
-
- data object EmptyJson : Error()
- data object CantFindSoundFile : Error()
- data class CorruptJsonFile(val reason: String) : Error()
-
- data object ShizukuNotStarted : Error()
- data object CantDetectKeyEventsInPhoneCall : Error()
-
- // This is returned from the PurchasingManager on FOSS builds that don't
- // have the pro features implemented.
- data object PurchasingNotImplemented : Error()
-
- data class ProductNotPurchased(val product: ProductId) : Error()
-
- sealed class PurchasingError : Error() {
- data object ProductNotFound : PurchasingError()
- data object Cancelled : PurchasingError()
- data object StoreProblem : PurchasingError()
- data object NetworkError : PurchasingError()
- data object PaymentPending : PurchasingError()
- data object PurchaseInvalid : PurchasingError()
- data class Unexpected(val message: String) : PurchasingError()
- }
-
- /**
- * DPAD triggers require a Key Mapper keyboard to be selected.
- */
- data object DpadTriggerImeNotSelected : Error()
- data object MalformedUrl : Error()
-
- data object UiElementNotFound : Error()
-}
-
-inline fun Result.onSuccess(f: (T) -> Unit): Result {
- if (this is Success) {
- f(this.value)
- }
-
- return this
-}
-
-inline fun Result.onFailure(f: (error: Error) -> U): Result {
- if (this is Error) {
- f(this)
- }
-
- return this
-}
-
-inline infix fun Result.then(f: (T) -> Result) = when (this) {
- is Success -> f(this.value)
- is Error -> this
-}
-
-suspend infix fun Result.suspendThen(f: suspend (T) -> Result) = when (this) {
- is Success -> f(this.value)
- is Error -> this
-}
-
-inline infix fun Result.otherwise(f: (error: Error) -> Result) = when (this) {
- is Success -> this
- is Error -> f(this)
-}
-
-inline fun Result.resolve(
- onSuccess: (value: T) -> U,
- onFailure: (error: Error) -> U,
-) = when (this) {
- is Success -> onSuccess(this.value)
- is Error -> onFailure(this)
-}
-
-inline infix fun Result.valueIfFailure(f: (error: Error) -> T): T = when (this) {
- is Success -> this.value
- is Error -> f(this)
-}
-
-fun Result.errorOrNull(): Error? {
- when (this) {
- is Error -> return this
- else -> Unit
- }
-
- return null
-}
-
-fun Result.valueOrNull(): T? {
- when (this) {
- is Success -> return this.value
- else -> Unit
- }
-
- return null
-}
-
-val Result.isError: Boolean
- get() = this is Error
-
-val Result.isSuccess: Boolean
- get() = this is Success
-
-fun Result.handle(onSuccess: (value: T) -> U, onError: (error: Error) -> U): U = when (this) {
- is Success -> onSuccess(value)
- is Error -> onError(this)
-}
-
-fun T.success() = Success(this)
diff --git a/app/src/main/java/io/github/sds100/keymapper/util/ServiceEvent.kt b/app/src/main/java/io/github/sds100/keymapper/util/ServiceEvent.kt
deleted file mode 100644
index 2cbc56b280..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/util/ServiceEvent.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-package io.github.sds100.keymapper.util
-
-import android.os.Parcelable
-import io.github.sds100.keymapper.actions.ActionData
-import io.github.sds100.keymapper.system.accessibility.RecordAccessibilityNodeState
-import io.github.sds100.keymapper.system.devices.InputDeviceInfo
-import io.github.sds100.keymapper.trigger.KeyEventDetectionSource
-import kotlinx.parcelize.Parcelize
-import kotlinx.serialization.Serializable
-
-@Serializable
-sealed class ServiceEvent {
-
- @Serializable
- data class Ping(val key: String) : ServiceEvent()
-
- @Serializable
- data class Pong(val key: String) : ServiceEvent()
-
- @Parcelize
- @Serializable
- data class RecordedTriggerKey(
- val keyCode: Int,
- val device: InputDeviceInfo?,
- val detectionSource: KeyEventDetectionSource,
- ) : ServiceEvent(),
- Parcelable
-
- @Serializable
- object StartRecordingTrigger : ServiceEvent()
-
- @Serializable
- object StopRecordingTrigger : ServiceEvent()
-
- @Serializable
- data class OnIncrementRecordTriggerTimer(val timeLeft: Int) : ServiceEvent()
-
- @Serializable
- object OnStoppedRecordingTrigger : ServiceEvent()
-
- @Serializable
- object OnHideKeyboardEvent : ServiceEvent()
-
- @Serializable
- object OnShowKeyboardEvent : ServiceEvent()
-
- @Serializable
- object HideKeyboard : ServiceEvent()
-
- @Serializable
- object ShowKeyboard : ServiceEvent()
-
- @Serializable
- data class TestAction(val action: ActionData) : ServiceEvent()
-
- @Serializable
- data class ChangeIme(val imeId: String) : ServiceEvent()
-
- @Serializable
- object DisableService : ServiceEvent()
-
- @Serializable
- object DismissLastNotification : ServiceEvent()
-
- @Serializable
- object DismissAllNotifications : ServiceEvent()
-
- @Serializable
- data class OnInputFocusChange(val isFocussed: Boolean) : ServiceEvent()
-
- @Serializable
- data class TriggerKeyMap(val uid: String) : ServiceEvent()
-
- @Serializable
- data class EnableInputMethod(val imeId: String) : ServiceEvent()
-
- @Serializable
- data object StartRecordingNodes : ServiceEvent()
-
- @Serializable
- data object StopRecordingNodes : ServiceEvent()
-
- @Serializable
- data class OnRecordNodeStateChanged(val state: RecordAccessibilityNodeState) : ServiceEvent()
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/util/ui/ISearchable.kt b/app/src/main/java/io/github/sds100/keymapper/util/ui/ISearchable.kt
deleted file mode 100644
index f74bcfac2b..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/util/ui/ISearchable.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package io.github.sds100.keymapper.util.ui
-
-/**
- * Created by sds100 on 13/01/21.
- */
-interface ISearchable {
- fun getSearchableString(): String
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/util/ui/ListItem.kt b/app/src/main/java/io/github/sds100/keymapper/util/ui/ListItem.kt
deleted file mode 100644
index 25ba907c59..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/util/ui/ListItem.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package io.github.sds100.keymapper.util.ui
-
-interface ListItem {
- val id: String
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/util/ui/NavResult.kt b/app/src/main/java/io/github/sds100/keymapper/util/ui/NavResult.kt
deleted file mode 100644
index f89f8efa7b..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/util/ui/NavResult.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package io.github.sds100.keymapper.util.ui
-
-/**
- * Created by sds100 on 23/03/2021.
- */
-data class NavResult(val key: String, val result: Any?)
diff --git a/app/src/main/java/io/github/sds100/keymapper/util/ui/NavigateEvent.kt b/app/src/main/java/io/github/sds100/keymapper/util/ui/NavigateEvent.kt
deleted file mode 100644
index 0f3d9f354f..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/util/ui/NavigateEvent.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package io.github.sds100.keymapper.util.ui
-
-/**
- * Created by sds100 on 25/07/2021.
- */
-data class NavigateEvent(val key: String, val destination: NavDestination<*>)
diff --git a/app/src/main/java/io/github/sds100/keymapper/util/ui/NavigationViewModel.kt b/app/src/main/java/io/github/sds100/keymapper/util/ui/NavigationViewModel.kt
deleted file mode 100644
index 510eb75792..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/util/ui/NavigationViewModel.kt
+++ /dev/null
@@ -1,340 +0,0 @@
-package io.github.sds100.keymapper.util.ui
-
-import android.os.Bundle
-import androidx.core.os.bundleOf
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.clearFragmentResultListener
-import androidx.fragment.app.setFragmentResultListener
-import androidx.lifecycle.lifecycleScope
-import androidx.navigation.fragment.findNavController
-import io.github.sds100.keymapper.NavAppDirections
-import io.github.sds100.keymapper.actions.ActionData
-import io.github.sds100.keymapper.actions.ChooseActionFragment
-import io.github.sds100.keymapper.actions.keyevent.ChooseKeyCodeFragment
-import io.github.sds100.keymapper.actions.keyevent.ConfigKeyEventActionFragment
-import io.github.sds100.keymapper.actions.pinchscreen.PinchPickCoordinateResult
-import io.github.sds100.keymapper.actions.pinchscreen.PinchPickDisplayCoordinateFragment
-import io.github.sds100.keymapper.actions.sound.ChooseSoundFileFragment
-import io.github.sds100.keymapper.actions.swipescreen.SwipePickCoordinateResult
-import io.github.sds100.keymapper.actions.swipescreen.SwipePickDisplayCoordinateFragment
-import io.github.sds100.keymapper.actions.tapscreen.PickCoordinateResult
-import io.github.sds100.keymapper.actions.tapscreen.PickDisplayCoordinateFragment
-import io.github.sds100.keymapper.actions.uielement.InteractUiElementFragment
-import io.github.sds100.keymapper.constraints.ChooseConstraintFragment
-import io.github.sds100.keymapper.constraints.Constraint
-import io.github.sds100.keymapper.system.apps.ActivityInfo
-import io.github.sds100.keymapper.system.apps.ChooseActivityFragment
-import io.github.sds100.keymapper.system.apps.ChooseAppFragment
-import io.github.sds100.keymapper.system.apps.ChooseAppShortcutFragment
-import io.github.sds100.keymapper.system.apps.ChooseAppShortcutResult
-import io.github.sds100.keymapper.system.bluetooth.BluetoothDeviceInfo
-import io.github.sds100.keymapper.system.bluetooth.ChooseBluetoothDeviceFragment
-import io.github.sds100.keymapper.system.intents.ConfigIntentFragment
-import io.github.sds100.keymapper.system.intents.ConfigIntentResult
-import io.github.sds100.keymapper.ui.utils.getJsonSerializable
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.SharedFlow
-import kotlinx.coroutines.flow.asSharedFlow
-import kotlinx.coroutines.flow.dropWhile
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.runBlocking
-import kotlinx.serialization.json.Json
-
-/**
- * Created by sds100 on 25/07/2021.
- */
-
-class NavigationViewModelImpl : NavigationViewModel {
- private val _onNavResult by lazy { MutableSharedFlow() }
- override val onNavResult by lazy { _onNavResult.asSharedFlow() }
-
- private val _navigate = MutableSharedFlow()
- override val navigate = _navigate.asSharedFlow()
-
- override suspend fun navigate(event: NavigateEvent) {
- // wait for the view to collect so navigating can happen
- _navigate.subscriptionCount.first { it > 0 }
-
- _navigate.emit(event)
- }
-
- override fun onNavResult(result: NavResult) {
- runBlocking { _onNavResult.emit(result) }
- }
-}
-
-interface NavigationViewModel {
- val navigate: SharedFlow
- val onNavResult: SharedFlow
-
- fun onNavResult(result: NavResult)
- suspend fun navigate(event: NavigateEvent)
-}
-
-suspend inline fun NavigationViewModel.navigate(
- key: String,
- destination: NavDestination,
-): R? {
- navigate(NavigateEvent(key, destination))
-
- /*
- This ensures only one job for a dialog is active at once by cancelling previous jobs when a new
- dialog is shown with the same key
- */
- return merge(
- navigate.dropWhile { it.key != key }.map { null },
- onNavResult.dropWhile { it.result !is R? && it.key != key }.map { it.result },
- ).first() as R?
-}
-
-/**
- * Must call in fragment's onCreate
- */
-fun NavigationViewModel.setupNavigation(fragment: Fragment) {
- val navigationSavedStateKey = "navigation:${this.javaClass.name}"
-
- val pendingResultsKeysExtra = "pending_results_keys"
- val pendingResultsDestinationsExtra = "pending_results_destinations"
-
- /**
- * Maps request keys to their destination.
- */
- val pendingResults = mutableMapOf()
-
- fragment.savedStateRegistry.registerSavedStateProvider(navigationSavedStateKey) {
- bundleOf(
- pendingResultsKeysExtra to pendingResults.keys.toTypedArray(),
- pendingResultsDestinationsExtra to pendingResults.values.toTypedArray(),
- )
- }
-
- fragment.savedStateRegistry.consumeRestoredStateForKey(navigationSavedStateKey)
- ?.let { bundle ->
- val oldPendingResultsKeys: Array =
- bundle.getStringArray(pendingResultsKeysExtra)!!
-
- val oldPendingResultsDestinations: Array =
- bundle.getStringArray(pendingResultsDestinationsExtra)!!
-
- oldPendingResultsKeys.forEachIndexed { i, requestKey ->
- val destination = oldPendingResultsDestinations[i]
-
- pendingResults[requestKey] = destination
-
- fragment.setFragmentResultListener(requestKey) { _, bundle ->
- sendNavResultFromBundle(requestKey, destination, bundle)
- }
- }
- }
-
- navigate.onEach { event ->
- val (requestKey, destination) = event
-
- pendingResults[requestKey] = destination.id
-
- fragment.clearFragmentResultListener(requestKey)
-
- fragment.setFragmentResultListener(requestKey) { _, bundle ->
- pendingResults.remove(event.key)
- sendNavResultFromBundle(event.key, event.destination.id, bundle)
- }
-
- val direction = when (destination) {
- is NavDestination.ChooseApp -> NavAppDirections.chooseApp(
- destination.allowHiddenApps,
- requestKey,
- )
-
- NavDestination.ChooseAppShortcut -> NavAppDirections.chooseAppShortcut(requestKey)
- NavDestination.ChooseKeyCode -> NavAppDirections.chooseKeyCode(requestKey)
- is NavDestination.ConfigKeyEventAction -> {
- val json = destination.action?.let {
- Json.encodeToString(it)
- }
-
- NavAppDirections.configKeyEvent(requestKey, json)
- }
-
- is NavDestination.PickCoordinate -> {
- val json = destination.result?.let {
- Json.encodeToString(it)
- }
-
- NavAppDirections.pickDisplayCoordinate(requestKey, json)
- }
-
- is NavDestination.PickSwipeCoordinate -> {
- val json = destination.result?.let {
- Json.encodeToString(it)
- }
-
- NavAppDirections.swipePickDisplayCoordinate(requestKey, json)
- }
-
- is NavDestination.PickPinchCoordinate -> {
- val json = destination.result?.let {
- Json.encodeToString(it)
- }
-
- NavAppDirections.pinchPickDisplayCoordinate(requestKey, json)
- }
-
- is NavDestination.ConfigIntent -> {
- val json = destination.result?.let {
- Json.encodeToString(it)
- }
-
- NavAppDirections.configIntent(requestKey, json)
- }
-
- is NavDestination.ChooseActivity -> NavAppDirections.chooseActivity(requestKey)
- is NavDestination.ChooseSound -> NavAppDirections.chooseSoundFile(requestKey)
- NavDestination.ChooseAction -> NavAppDirections.toChooseActionFragment(requestKey)
- is NavDestination.ChooseConstraint -> NavAppDirections.chooseConstraint(
- requestKey = requestKey,
- )
-
- is NavDestination.ChooseBluetoothDevice -> NavAppDirections.chooseBluetoothDevice(
- requestKey,
- )
-
- NavDestination.About -> NavAppDirections.actionGlobalAboutFragment()
- NavDestination.Settings -> NavAppDirections.toSettingsFragment()
-
- is NavDestination.ConfigKeyMap -> when (destination) {
- is NavDestination.ConfigKeyMap.New ->
- NavAppDirections.actionToConfigKeymap(
- groupUid = destination.groupUid,
- showAdvancedTriggers = destination.showAdvancedTriggers,
- )
-
- is NavDestination.ConfigKeyMap.Open ->
- NavAppDirections.actionToConfigKeymap(
- keyMapUid = destination.keyMapUid,
- showAdvancedTriggers = destination.showAdvancedTriggers,
- )
- }
-
- is NavDestination.ChooseFloatingLayout -> NavAppDirections.toChooseFloatingLayoutFragment()
- NavDestination.ShizukuSettings -> NavAppDirections.toShizukuSettingsFragment()
- is NavDestination.ConfigFloatingButton -> NavAppDirections.toConfigFloatingButton(
- destination.buttonUid,
- )
-
- is NavDestination.InteractUiElement -> NavAppDirections.interactUiElement(
- requestKey = requestKey,
- action = destination.action?.let { Json.encodeToString(destination.action) },
- )
- }
-
- fragment.findNavController().navigate(direction)
- }.launchIn(fragment.lifecycleScope)
-}
-
-fun NavigationViewModel.sendNavResultFromBundle(
- requestKey: String,
- destinationId: String,
- bundle: Bundle,
-) {
- when (destinationId) {
- NavDestination.ID_CHOOSE_APP -> {
- val packageName = bundle.getString(ChooseAppFragment.EXTRA_PACKAGE_NAME)
-
- onNavResult(NavResult(requestKey, packageName!!))
- }
-
- NavDestination.ID_CHOOSE_APP_SHORTCUT -> {
- val result = bundle.getJsonSerializable(
- ChooseAppShortcutFragment.EXTRA_RESULT,
- )
-
- onNavResult(NavResult(requestKey, result))
- }
-
- NavDestination.ID_KEY_CODE -> {
- val keyCode = bundle.getInt(ChooseKeyCodeFragment.EXTRA_KEYCODE)
-
- onNavResult(NavResult(requestKey, keyCode))
- }
-
- NavDestination.ID_KEY_EVENT -> {
- val json = bundle.getString(ConfigKeyEventActionFragment.EXTRA_RESULT)!!
- val keyEventAction = Json.decodeFromString(json)
-
- onNavResult(NavResult(requestKey, keyEventAction))
- }
-
- NavDestination.ID_PICK_COORDINATE -> {
- val json = bundle.getString(PickDisplayCoordinateFragment.EXTRA_RESULT)!!
- val result = Json.decodeFromString(json)
-
- onNavResult(NavResult(requestKey, result))
- }
-
- NavDestination.ID_PICK_SWIPE_COORDINATE -> {
- val json = bundle.getString(SwipePickDisplayCoordinateFragment.EXTRA_RESULT)!!
- val result = Json.decodeFromString(json)
-
- onNavResult(NavResult(requestKey, result))
- }
-
- NavDestination.ID_PICK_PINCH_COORDINATE -> {
- val json = bundle.getString(PinchPickDisplayCoordinateFragment.EXTRA_RESULT)!!
- val result = Json.decodeFromString(json)
-
- onNavResult(NavResult(requestKey, result))
- }
-
- NavDestination.ID_CONFIG_INTENT -> {
- val json = bundle.getString(ConfigIntentFragment.EXTRA_RESULT)!!
- val result = Json.decodeFromString(json)
-
- onNavResult(NavResult(requestKey, result))
- }
-
- NavDestination.ID_CHOOSE_ACTIVITY -> {
- val json = bundle.getString(ChooseActivityFragment.EXTRA_RESULT)!!
- val result = Json.decodeFromString(json)
-
- onNavResult(NavResult(requestKey, result))
- }
-
- NavDestination.ID_CHOOSE_SOUND -> {
- val json = bundle.getString(ChooseSoundFileFragment.EXTRA_RESULT)!!
- val result = Json.decodeFromString(json)
-
- onNavResult(NavResult(requestKey, result))
- }
-
- NavDestination.ID_CHOOSE_ACTION -> {
- val json = bundle.getString(ChooseActionFragment.EXTRA_ACTION)!!
- val action = Json.decodeFromString(json)
-
- onNavResult(NavResult(requestKey, action))
- }
-
- NavDestination.ID_CHOOSE_CONSTRAINT -> {
- val json = bundle.getString(ChooseConstraintFragment.EXTRA_CONSTRAINT)!!
- val constraint = Json.decodeFromString(json)
-
- onNavResult(NavResult(requestKey, constraint))
- }
-
- NavDestination.ID_CHOOSE_BLUETOOTH_DEVICE -> {
- val address = bundle.getString(ChooseBluetoothDeviceFragment.EXTRA_ADDRESS)!!
- val name = bundle.getString(ChooseBluetoothDeviceFragment.EXTRA_NAME)!!
-
- onNavResult(NavResult(requestKey, BluetoothDeviceInfo(address, name)))
- }
-
- NavDestination.ID_INTERACT_UI_ELEMENT_ACTION -> {
- val json = bundle.getString(InteractUiElementFragment.EXTRA_ACTION)!!
- val result = Json.decodeFromString(json)
- onNavResult(NavResult(requestKey, result))
- }
- }
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/util/ui/OnPopupResponseEvent.kt b/app/src/main/java/io/github/sds100/keymapper/util/ui/OnPopupResponseEvent.kt
deleted file mode 100644
index bcf274a7d4..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/util/ui/OnPopupResponseEvent.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package io.github.sds100.keymapper.util.ui
-
-/**
- * Created by sds100 on 23/03/2021.
- */
-data class OnPopupResponseEvent(val key: String, val response: Any?)
diff --git a/app/src/main/java/io/github/sds100/keymapper/util/ui/ShowPopupEvent.kt b/app/src/main/java/io/github/sds100/keymapper/util/ui/ShowPopupEvent.kt
deleted file mode 100644
index fdf17c5ce7..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/util/ui/ShowPopupEvent.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package io.github.sds100.keymapper.util.ui
-
-/**
- * Created by sds100 on 23/03/2021.
- */
-data class ShowPopupEvent(val key: String, val ui: PopupUi<*>)
diff --git a/app/src/main/java/io/github/sds100/keymapper/util/ui/SnackBarUtils.kt b/app/src/main/java/io/github/sds100/keymapper/util/ui/SnackBarUtils.kt
deleted file mode 100644
index c7e283e2a5..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/util/ui/SnackBarUtils.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-package io.github.sds100.keymapper.util.ui
-
-import androidx.coordinatorlayout.widget.CoordinatorLayout
-import io.github.sds100.keymapper.R
-import kotlinx.coroutines.suspendCancellableCoroutine
-import splitties.snackbar.action
-import splitties.snackbar.longSnack
-import splitties.snackbar.onDismiss
-import splitties.snackbar.snack
-import kotlin.coroutines.resume
-
-/**
- * Created by sds100 on 06/04/2021.
- */
-object SnackBarUtils {
-
- suspend fun show(
- view: CoordinatorLayout,
- text: String,
- actionText: String? = null,
- long: Boolean = false,
- ) =
- suspendCancellableCoroutine { continuation ->
-
- val snackBar = if (long) {
- view.longSnack(text) {
- if (actionText != null) {
- action(actionText) {
- if (!continuation.isCompleted) {
- continuation.resume(Unit)
- }
- }
- }
-
- anchorView = view.findViewById(R.id.fab)
- }
- } else {
- view.snack(text) {
- if (actionText != null) {
- action(actionText) {
- if (!continuation.isCompleted) {
- continuation.resume(Unit)
- }
- }
- }
-
- anchorView = view.findViewById(R.id.fab)
- }
- }
-
- // if there is no action then there is no point waiting for a user response
- if (actionText == null) {
- continuation.resume(null)
- }
-
- snackBar.onDismiss {
- if (!continuation.isCompleted) {
- continuation.resume(null)
- }
- }
- }
-}
diff --git a/app/src/main/java/io/github/sds100/keymapper/util/ui/compose/icons/KeyMapperIcons.kt b/app/src/main/java/io/github/sds100/keymapper/util/ui/compose/icons/KeyMapperIcons.kt
deleted file mode 100644
index 343b292720..0000000000
--- a/app/src/main/java/io/github/sds100/keymapper/util/ui/compose/icons/KeyMapperIcons.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package io.github.sds100.keymapper.util.ui.compose.icons
-
-object KeyMapperIcons
diff --git a/app/src/main/res/menu/menu_config_mapping.xml b/app/src/main/res/menu/menu_config_mapping.xml
deleted file mode 100644
index f1dce6e2aa..0000000000
--- a/app/src/main/res/menu/menu_config_mapping.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/navigation/nav_config_keymap.xml b/app/src/main/res/navigation/nav_config_keymap.xml
deleted file mode 100644
index 4ea4136136..0000000000
--- a/app/src/main/res/navigation/nav_config_keymap.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
deleted file mode 100644
index 1d669e715e..0000000000
--- a/app/src/main/res/values-ar/strings.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
deleted file mode 100644
index 1d669e715e..0000000000
--- a/app/src/main/res/values-cs/strings.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
deleted file mode 100644
index 1d669e715e..0000000000
--- a/app/src/main/res/values-de/strings.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
deleted file mode 100644
index 1d669e715e..0000000000
--- a/app/src/main/res/values-es/strings.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
deleted file mode 100644
index 1d669e715e..0000000000
--- a/app/src/main/res/values-fr/strings.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
deleted file mode 100644
index 96247417f6..0000000000
--- a/app/src/main/res/values-hu/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-
-
- Engedd el a kulcsokat!
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml
deleted file mode 100644
index 1d669e715e..0000000000
--- a/app/src/main/res/values-id/strings.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml
deleted file mode 100644
index 1d669e715e..0000000000
--- a/app/src/main/res/values-ka/strings.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
deleted file mode 100644
index 1d669e715e..0000000000
--- a/app/src/main/res/values-ko/strings.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
deleted file mode 100644
index 1d669e715e..0000000000
--- a/app/src/main/res/values-pl/strings.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
deleted file mode 100644
index 1d669e715e..0000000000
--- a/app/src/main/res/values-pt/strings.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
deleted file mode 100644
index 1d669e715e..0000000000
--- a/app/src/main/res/values-ru/strings.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
deleted file mode 100644
index 1d669e715e..0000000000
--- a/app/src/main/res/values-sk/strings.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
deleted file mode 100644
index 1d669e715e..0000000000
--- a/app/src/main/res/values-uk/strings.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
deleted file mode 100644
index 1d669e715e..0000000000
--- a/app/src/main/res/values-vi/strings.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
deleted file mode 100644
index 1d669e715e..0000000000
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
deleted file mode 100644
index 1d669e715e..0000000000
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/test/java/io/github/sds100/keymapper/util/JsonTestUtils.kt b/app/src/test/java/io/github/sds100/keymapper/util/JsonTestUtils.kt
deleted file mode 100644
index 71e2d52856..0000000000
--- a/app/src/test/java/io/github/sds100/keymapper/util/JsonTestUtils.kt
+++ /dev/null
@@ -1,88 +0,0 @@
-package io.github.sds100.keymapper.util
-
-import com.github.salomonbrys.kotson.contains
-import com.github.salomonbrys.kotson.forEach
-import com.github.salomonbrys.kotson.get
-import com.google.gson.JsonArray
-import com.google.gson.JsonElement
-import com.google.gson.JsonObject
-import com.google.gson.JsonPrimitive
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.core.Is.`is`
-import org.junit.Assert
-
-/**
- * Created by sds100 on 25/01/21.
- */
-object JsonTestUtils {
- private const val NAME_SEPARATOR = '/'
-
- fun compareBothWays(element: JsonElement, elementName: String, other: JsonElement, otherName: String) {
- compare("", element, elementName, other, otherName)
- compare("", other, elementName, element, elementName)
- }
-
- private fun compare(parentNamePath: String = "", element: JsonElement, elementName: String, rootToCompare: JsonElement, rootName: String) {
- when (element) {
- is JsonObject -> {
- element.forEach { name, jsonElement ->
- val newPath = if (parentNamePath.isBlank()) {
- name
- } else {
- "$parentNamePath$NAME_SEPARATOR$name"
- }
-
- compare(newPath, jsonElement, elementName, rootToCompare, rootName)
- }
- }
-
- is JsonArray -> {
- val pathToArrayToCompare = parentNamePath.split(NAME_SEPARATOR)
- var arrayToCompare: JsonArray? = null
-
- var parentElement: JsonElement = rootToCompare
- pathToArrayToCompare.forEach {
- if (it == "") return@forEach
-
- parentElement = parentElement[it]
- }
-
- if (parentElement is JsonArray) {
- arrayToCompare = parentElement as JsonArray
- }
-
- Assert.assertNotNull("can't find array $elementName/$parentNamePath in $rootName", arrayToCompare)
- arrayToCompare ?: return
-
- element.forEachIndexed { index, arrayElement ->
- val validIndex = index <= arrayToCompare.toList().lastIndex
-
- assertThat("$rootName/${pathToArrayToCompare.last()} doesn't contain $arrayElement at $index index", validIndex)
-
- compare("", arrayElement, "$elementName/${pathToArrayToCompare.last()}", arrayToCompare[index]!!, "$rootName/${pathToArrayToCompare.last()}")
- }
- }
-
- is JsonPrimitive -> {
- val names = parentNamePath.split(NAME_SEPARATOR)
- var parentElement: JsonElement = rootToCompare
-
- if (names == listOf("")) {
- assertThat("$elementName/:$element doesn't match $rootName/:$parentElement", (parentElement), `is`(element))
- } else {
- names.forEachIndexed { index, name ->
- if (parentElement is JsonObject) {
- assertThat("$elementName/$parentNamePath not found in $rootName", (parentElement as JsonObject).contains(name))
- }
-
- parentElement = parentElement[name]
-
- if (index == names.lastIndex) {
- assertThat("$elementName/$parentNamePath:$element doesn't match $rootName/$parentNamePath:$parentElement", (parentElement as JsonPrimitive), `is`(element))
- }
- }
- }
- }
- }
- }
-}
diff --git a/app/version.properties b/app/version.properties
index 9702277f1d..67a33d0fe4 100644
--- a/app/version.properties
+++ b/app/version.properties
@@ -1,3 +1,3 @@
-VERSION_NAME=3.1.2
+VERSION_NAME=3.2.0
VERSION_CODE=122
VERSION_NUM=0
\ No newline at end of file
diff --git a/base/.gitignore b/base/.gitignore
new file mode 100644
index 0000000000..42afabfd2a
--- /dev/null
+++ b/base/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/base/build.gradle.kts b/base/build.gradle.kts
new file mode 100644
index 0000000000..7464caa89b
--- /dev/null
+++ b/base/build.gradle.kts
@@ -0,0 +1,154 @@
+import java.util.Properties
+
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.kotlin.compose)
+ alias(libs.plugins.kotlin.kapt)
+ alias(libs.plugins.kotlin.serialization)
+ alias(libs.plugins.kotlin.parcelize)
+ alias(libs.plugins.androidx.navigation.safeargs.kotlin)
+ alias(libs.plugins.google.devtools.ksp)
+ alias(libs.plugins.jlleitschuh.gradle.ktlint)
+ alias(libs.plugins.dagger.hilt.android)
+}
+
+android {
+ namespace = "io.github.sds100.keymapper.base"
+ compileSdk = libs.versions.compile.sdk.get().toInt()
+
+ val versionProperties = Properties().apply {
+ project.file("../app/version.properties").inputStream().use { load(it) }
+ }
+
+ defaultConfig {
+ minSdk = libs.versions.min.sdk.get().toInt()
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles("consumer-rules.pro")
+
+ buildConfigField(
+ "Integer",
+ "VERSION_CODE",
+ versionProperties.getProperty("VERSION_CODE"),
+ )
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = true
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro",
+ )
+ }
+ }
+
+ buildFeatures {
+ dataBinding = true
+ viewBinding = true
+ aidl = true
+ buildConfig = true
+ compose = true
+ }
+
+ compileOptions {
+ isCoreLibraryDesugaringEnabled = true
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+
+ kapt {
+ correctErrorTypes = true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
+ }
+}
+
+dependencies {
+ implementation(project(":common"))
+ implementation(project(":data"))
+ implementation(project(":system"))
+ implementation(project(":systemstubs"))
+
+ // kotlin stuff
+ implementation(libs.kotlinx.coroutines.android)
+ implementation(libs.kotlinx.serialization.json)
+
+ // random stuff
+ implementation(libs.google.android.material)
+ implementation(libs.kotson)
+ implementation(libs.airbnb.epoxy)
+ implementation(libs.airbnb.epoxy.databinding)
+ debugImplementation(libs.androidx.ui.tooling)
+ kapt(libs.airbnb.epoxy.processor)
+ implementation(libs.jakewharton.timber)
+ implementation(libs.anggrayudi.storage)
+ implementation(libs.github.mflisar.dragselectrecyclerview)
+ implementation(libs.google.flexbox)
+ implementation(libs.squareup.okhttp)
+ coreLibraryDesugaring(libs.desugar.jdk.libs)
+ implementation(libs.canopas.introshowcaseview)
+ implementation(libs.dagger.hilt.android)
+ ksp(libs.dagger.hilt.android.compiler)
+ implementation(libs.bundles.splitties)
+
+ // androidx
+ implementation(libs.androidx.legacy.support.core.ui)
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.activity.ktx)
+ implementation(libs.androidx.fragment.ktx)
+ implementation(libs.bundles.androidx.lifecycle)
+ implementation(libs.bundles.androidx.navigation)
+ implementation(libs.androidx.multidex)
+ implementation(libs.androidx.appcompat)
+ implementation(libs.androidx.recyclerview)
+ implementation(libs.androidx.preference.ktx)
+ implementation(libs.androidx.constraintlayout)
+ implementation(libs.androidx.lifecycle.extensions) // Note: Deprecated
+ implementation(libs.androidx.viewpager2)
+ implementation(libs.androidx.datastore.preferences)
+ implementation(libs.androidx.core.splashscreen)
+ implementation(libs.androidx.hilt.navigation.compose)
+
+ // Compose
+ implementation(platform(libs.androidx.compose.bom))
+ implementation(libs.androidx.compose.foundation)
+ implementation(libs.androidx.compose.ui.android)
+ implementation(libs.androidx.compose.material3)
+ implementation(libs.androidx.compose.ui.tooling.preview)
+ implementation(libs.androidx.compose.material.icons.extended)
+ implementation(libs.androidx.compose.material3.adaptive)
+ implementation(libs.androidx.compose.material3.adaptive.navigation)
+ implementation(libs.google.accompanist.drawablepainter)
+ implementation(libs.androidx.compose.ui.tooling)
+
+ // Tests
+
+ testImplementation(libs.junit)
+ testImplementation(libs.hamcrest.all)
+ testImplementation(libs.androidx.junit.ktx) // androidx.test.ext:junit-ktx
+ testImplementation(libs.androidx.test.core.ktx)
+ testImplementation(libs.androidx.test.core)
+ testImplementation(libs.robolectric)
+ testImplementation(libs.androidx.arch.core.testing)
+ testImplementation(libs.kotlinx.coroutines.test)
+ testImplementation(libs.junit.params)
+ testImplementation(libs.mockito.kotlin)
+ testImplementation(libs.mockito.core)
+ testImplementation(libs.mockito.inline)
+ testDebugImplementation(libs.androidx.fragment.testing)
+
+ // Dependencies for Android instrumented tests
+ androidTestImplementation(libs.androidx.test.ext.junit) // androidx.test.ext:junit
+ androidTestImplementation(libs.junit) // Repeated, fine
+ androidTestImplementation(libs.androidx.navigation.testing)
+ androidTestImplementation(libs.androidx.room.testing.legacy)
+ androidTestImplementation(libs.mockito.android)
+}
diff --git a/app/src/test/resources/backup-manager-test/restore-all.zip/sounds/sound.ogg b/base/consumer-rules.pro
similarity index 100%
rename from app/src/test/resources/backup-manager-test/restore-all.zip/sounds/sound.ogg
rename to base/consumer-rules.pro
diff --git a/base/proguard-rules.pro b/base/proguard-rules.pro
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/base/src/main/AndroidManifest.xml b/base/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..bf276a36b2
--- /dev/null
+++ b/base/src/main/AndroidManifest.xml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/assets/whats-new.txt b/base/src/main/assets/whats-new.txt
similarity index 100%
rename from app/src/main/assets/whats-new.txt
rename to base/src/main/assets/whats-new.txt
diff --git a/base/src/main/java/io/github/sds100/keymapper/base/ActivityViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/ActivityViewModel.kt
new file mode 100644
index 0000000000..a8b48bb2b9
--- /dev/null
+++ b/base/src/main/java/io/github/sds100/keymapper/base/ActivityViewModel.kt
@@ -0,0 +1,34 @@
+package io.github.sds100.keymapper.base
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import dagger.hilt.android.lifecycle.HiltViewModel
+import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider
+import io.github.sds100.keymapper.base.utils.navigation.NavigationProviderImpl
+import io.github.sds100.keymapper.base.utils.ui.DialogProvider
+import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
+import io.github.sds100.keymapper.base.utils.ui.ViewModelHelper
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class ActivityViewModel @Inject constructor(
+ resourceProvider: ResourceProvider,
+ dialogProvider: DialogProvider,
+) : ViewModel(),
+ ResourceProvider by resourceProvider,
+ DialogProvider by dialogProvider,
+ NavigationProvider by NavigationProviderImpl() {
+
+ var handledActivityLaunchIntent: Boolean = false
+ var previousNightMode: Int? = null
+
+ fun onCantFindAccessibilitySettings() {
+ viewModelScope.launch {
+ ViewModelHelper.handleCantFindAccessibilitySettings(
+ resourceProvider = this@ActivityViewModel,
+ dialogProvider = this@ActivityViewModel,
+ )
+ }
+ }
+}
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
new file mode 100644
index 0000000000..62d85ba466
--- /dev/null
+++ b/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt
@@ -0,0 +1,190 @@
+package io.github.sds100.keymapper.base
+
+import android.annotation.SuppressLint
+import android.content.Intent
+import android.os.Build
+import android.os.UserManager
+import android.util.Log
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatDelegate
+import androidx.core.content.getSystemService
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleObserver
+import androidx.lifecycle.OnLifecycleEvent
+import androidx.lifecycle.ProcessLifecycleOwner
+import androidx.multidex.MultiDexApplication
+import io.github.sds100.keymapper.base.logging.KeyMapperLoggingTree
+import io.github.sds100.keymapper.base.settings.ThemeUtils
+import io.github.sds100.keymapper.base.system.accessibility.AccessibilityServiceAdapterImpl
+import io.github.sds100.keymapper.base.system.inputmethod.AutoSwitchImeController
+import io.github.sds100.keymapper.base.system.notifications.NotificationController
+import io.github.sds100.keymapper.base.system.permissions.AutoGrantPermissionController
+import io.github.sds100.keymapper.data.Keys
+import io.github.sds100.keymapper.data.entities.LogEntryEntity
+import io.github.sds100.keymapper.data.repositories.LogRepository
+import io.github.sds100.keymapper.data.repositories.SettingsPreferenceRepository
+import io.github.sds100.keymapper.system.apps.AndroidPackageManagerAdapter
+import io.github.sds100.keymapper.system.devices.AndroidDevicesAdapter
+import io.github.sds100.keymapper.system.permissions.AndroidPermissionAdapter
+import io.github.sds100.keymapper.system.permissions.Permission
+import io.github.sds100.keymapper.system.root.SuAdapterImpl
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import timber.log.Timber
+import java.util.Calendar
+import javax.inject.Inject
+
+@SuppressLint("LogNotTimber")
+abstract class BaseKeyMapperApp : MultiDexApplication() {
+ private val tag = BaseKeyMapperApp::class.simpleName
+
+ @Inject
+ lateinit var appCoroutineScope: CoroutineScope
+
+ @Inject
+ lateinit var notificationController: NotificationController
+
+ @Inject
+ lateinit var autoSwitchImeController: AutoSwitchImeController
+
+ @Inject
+ lateinit var packageManagerAdapter: AndroidPackageManagerAdapter
+
+ @Inject
+ lateinit var devicesAdapter: AndroidDevicesAdapter
+
+ @Inject
+ lateinit var permissionAdapter: AndroidPermissionAdapter
+
+ @Inject
+ lateinit var accessibilityServiceAdapter: AccessibilityServiceAdapterImpl
+
+ @Inject
+ lateinit var suAdapter: SuAdapterImpl
+
+ @Inject
+ lateinit var autoGrantPermissionController: AutoGrantPermissionController
+
+ @Inject
+ lateinit var loggingTree: KeyMapperLoggingTree
+
+ @Inject
+ lateinit var settingsRepository: SettingsPreferenceRepository
+
+ @Inject
+ lateinit var logRepository: LogRepository
+
+ private val processLifecycleOwner by lazy { ProcessLifecycleOwner.get() }
+
+ private val userManager: UserManager? by lazy { getSystemService() }
+
+ private val initLock: Any = Any()
+ private var initialized = false
+
+ override fun onCreate() {
+ val priorExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
+
+ 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)
+ }
+
+ priorExceptionHandler?.uncaughtException(thread, exception)
+ }
+
+ super.onCreate()
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && userManager?.isUserUnlocked == false) {
+ Log.i(tag, "KeyMapperApp: Delay init because locked.")
+ // If the device is still encrypted and locked do not initialize anything that
+ // may potentially need the encrypted app storage like databases.
+ return
+ }
+
+ synchronized(initLock) {
+ init()
+ initialized = true
+ }
+ }
+
+ fun onBootUnlocked() {
+ synchronized(initLock) {
+ if (!initialized) {
+ init()
+ }
+ initialized = true
+ }
+ }
+
+ private fun init() {
+ Log.i(tag, "KeyMapperApp: Init")
+
+ settingsRepository.get(Keys.darkTheme)
+ .map { it?.toIntOrNull() }
+ .map {
+ when (it) {
+ ThemeUtils.DARK -> AppCompatDelegate.MODE_NIGHT_YES
+ ThemeUtils.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
+ else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
+ }
+ }
+ .onEach { mode -> AppCompatDelegate.setDefaultNightMode(mode) }
+ .launchIn(appCoroutineScope)
+
+ if (BuildConfig.BUILD_TYPE == "debug" || BuildConfig.BUILD_TYPE == "debug_release") {
+ Timber.plant(Timber.DebugTree())
+ }
+
+ Timber.plant(loggingTree)
+
+ notificationController.init()
+
+ autoSwitchImeController.init()
+
+ processLifecycleOwner.lifecycle.addObserver(object : LifecycleObserver {
+ @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()
+
+ if (BuildConfig.DEBUG && permissionAdapter.isGranted(Permission.WRITE_SECURE_SETTINGS)) {
+ accessibilityServiceAdapter.start()
+ }
+ }
+ })
+
+ appCoroutineScope.launch {
+ notificationController.openApp.collectLatest { intentAction ->
+ Intent(this@BaseKeyMapperApp, getMainActivityClass()).apply {
+ action = intentAction
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+
+ startActivity(this)
+ }
+ }
+ }
+
+ notificationController.showToast.onEach { toast ->
+ Toast.makeText(this, toast, Toast.LENGTH_SHORT).show()
+ }.launchIn(appCoroutineScope)
+
+ autoGrantPermissionController.start()
+ }
+
+ abstract fun getMainActivityClass(): Class<*>
+}
diff --git a/app/src/main/java/io/github/sds100/keymapper/BaseMainActivity.kt b/base/src/main/java/io/github/sds100/keymapper/base/BaseMainActivity.kt
similarity index 72%
rename from app/src/main/java/io/github/sds100/keymapper/BaseMainActivity.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/BaseMainActivity.kt
index 8aed150c4f..6c6dae42eb 100644
--- a/app/src/main/java/io/github/sds100/keymapper/BaseMainActivity.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/BaseMainActivity.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper
+package io.github.sds100.keymapper.base
import android.content.BroadcastReceiver
import android.content.Context
@@ -17,7 +17,6 @@ import androidx.compose.ui.graphics.toArgb
import androidx.core.content.ContextCompat
import androidx.core.content.IntentCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
-import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
@@ -26,59 +25,69 @@ import androidx.navigation.findNavController
import com.anggrayudi.storage.extension.openInputStream
import com.anggrayudi.storage.extension.openOutputStream
import com.anggrayudi.storage.extension.toDocumentFile
-import io.github.sds100.keymapper.Constants.PACKAGE_NAME
-import io.github.sds100.keymapper.compose.ComposeColors
-import io.github.sds100.keymapper.databinding.ActivityMainBinding
-import io.github.sds100.keymapper.system.accessibility.AccessibilityServiceAdapter
+import io.github.sds100.keymapper.base.compose.ComposeColors
+import io.github.sds100.keymapper.base.onboarding.OnboardingUseCase
+import io.github.sds100.keymapper.base.system.accessibility.AccessibilityServiceAdapterImpl
+import io.github.sds100.keymapper.base.system.permissions.RequestPermissionDelegate
+import io.github.sds100.keymapper.base.trigger.RecordTriggerController
+import io.github.sds100.keymapper.base.utils.ui.ResourceProviderImpl
+import io.github.sds100.keymapper.base.utils.ui.launchRepeatOnLifecycle
+import io.github.sds100.keymapper.common.BuildConfigProvider
import io.github.sds100.keymapper.system.files.FileUtils
import io.github.sds100.keymapper.system.inputevents.MyMotionEvent
+import io.github.sds100.keymapper.system.notifications.NotificationReceiverAdapterImpl
import io.github.sds100.keymapper.system.permissions.AndroidPermissionAdapter
-import io.github.sds100.keymapper.system.permissions.RequestPermissionDelegate
-import io.github.sds100.keymapper.trigger.RecordTriggerController
-import io.github.sds100.keymapper.util.launchRepeatOnLifecycle
-import io.github.sds100.keymapper.util.ui.showPopups
+import io.github.sds100.keymapper.system.shizuku.ShizukuAdapter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import timber.log.Timber
-
-/**
- * Created by sds100 on 19/02/2020.
- */
+import javax.inject.Inject
abstract class BaseMainActivity : AppCompatActivity() {
companion object {
const val ACTION_SHOW_ACCESSIBILITY_SETTINGS_NOT_FOUND_DIALOG =
- "$PACKAGE_NAME.ACTION_SHOW_ACCESSIBILITY_SETTINGS_NOT_FOUND_DIALOG"
+ "${BuildConfig.LIBRARY_PACKAGE_NAME}.ACTION_SHOW_ACCESSIBILITY_SETTINGS_NOT_FOUND_DIALOG"
const val ACTION_USE_FLOATING_BUTTONS =
- "$PACKAGE_NAME.ACTION_USE_FLOATING_BUTTONS"
+ "${BuildConfig.LIBRARY_PACKAGE_NAME}.ACTION_USE_FLOATING_BUTTONS"
- const val ACTION_SAVE_FILE = "$PACKAGE_NAME.ACTION_SAVE_FILE"
- const val EXTRA_FILE_URI = "$PACKAGE_NAME.EXTRA_FILE_URI"
+ const val ACTION_SAVE_FILE = "${BuildConfig.LIBRARY_PACKAGE_NAME}.ACTION_SAVE_FILE"
+ const val EXTRA_FILE_URI = "${BuildConfig.LIBRARY_PACKAGE_NAME}.EXTRA_FILE_URI"
}
- private val permissionAdapter: AndroidPermissionAdapter by lazy {
- ServiceLocator.permissionAdapter(this)
- }
+ @Inject
+ lateinit var permissionAdapter: AndroidPermissionAdapter
- val serviceAdapter: AccessibilityServiceAdapter by lazy {
- ServiceLocator.accessibilityServiceAdapter(this)
- }
+ @Inject
+ lateinit var serviceAdapter: AccessibilityServiceAdapterImpl
- val viewModel by viewModels {
- ActivityViewModel.Factory(ServiceLocator.resourceProvider(this))
- }
+ @Inject
+ lateinit var resourceProvider: ResourceProviderImpl
+
+ @Inject
+ lateinit var onboardingUseCase: OnboardingUseCase
+
+ @Inject
+ lateinit var recordTriggerController: RecordTriggerController
+
+ @Inject
+ lateinit var notificationReceiverAdapter: NotificationReceiverAdapterImpl
+
+ @Inject
+ lateinit var shizukuAdapter: ShizukuAdapter
+
+ @Inject
+ lateinit var buildConfigProvider: BuildConfigProvider
+
+ private lateinit var requestPermissionDelegate: RequestPermissionDelegate
private val currentNightMode: Int
get() = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
- private lateinit var requestPermissionDelegate: RequestPermissionDelegate
- private val recordTriggerController: RecordTriggerController by lazy {
- (applicationContext as KeyMapperApp).recordTriggerController
- }
+ val viewModel by viewModels()
private var originalFileUri: Uri? = null
@@ -124,17 +133,19 @@ abstract class BaseMainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
if (viewModel.previousNightMode != currentNightMode) {
- ServiceLocator.resourceProvider(this).onThemeChange()
+ resourceProvider.onThemeChange()
}
- val binding =
- DataBindingUtil.setContentView(this, R.layout.activity_main)
-
- viewModel.showPopups(this, binding.coordinatorLayout)
-
- requestPermissionDelegate = RequestPermissionDelegate(this, showDialogs = true)
+ requestPermissionDelegate = RequestPermissionDelegate(
+ this,
+ showDialogs = true,
+ permissionAdapter,
+ notificationReceiverAdapter = notificationReceiverAdapter,
+ buildConfigProvider = buildConfigProvider,
+ shizukuAdapter = shizukuAdapter,
+ )
- ServiceLocator.permissionAdapter(this@BaseMainActivity).request
+ permissionAdapter.request
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach { permission ->
requestPermissionDelegate.requestPermission(
@@ -174,18 +185,18 @@ abstract class BaseMainActivity : AppCompatActivity() {
override fun onResume() {
super.onResume()
- Timber.i("MainActivity: onResume. Version: ${Constants.VERSION}")
+ Timber.i("MainActivity: onResume. Version: ${buildConfigProvider.version} ${buildConfigProvider.versionCode}")
// This must be after onResume to ensure all the fragment lifecycles' have also
// resumed which are observing these events.
// This is checked here and not in KeyMapperApp's lifecycle observer because
// the activities have not necessarily resumed at that point.
permissionAdapter.onPermissionsChanged()
- serviceAdapter.updateWhetherServiceIsEnabled()
+ serviceAdapter.invalidateState()
}
override fun onDestroy() {
- UseCases.onboarding(this).shownAppIntro = true
+ onboardingUseCase.shownAppIntro = true
viewModel.previousNightMode = currentNightMode
unregisterReceiver(broadcastReceiver)
diff --git a/base/src/main/java/io/github/sds100/keymapper/base/BaseMainNavHost.kt b/base/src/main/java/io/github/sds100/keymapper/base/BaseMainNavHost.kt
new file mode 100644
index 0000000000..4589823952
--- /dev/null
+++ b/base/src/main/java/io/github/sds100/keymapper/base/BaseMainNavHost.kt
@@ -0,0 +1,59 @@
+package io.github.sds100.keymapper.base
+
+import androidx.compose.animation.AnimatedContentTransitionScope
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.NavHostController
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import io.github.sds100.keymapper.base.actions.uielement.InteractUiElementScreen
+import io.github.sds100.keymapper.base.actions.uielement.InteractUiElementViewModel
+import io.github.sds100.keymapper.base.constraints.ChooseConstraintScreen
+import io.github.sds100.keymapper.base.constraints.ChooseConstraintViewModel
+import io.github.sds100.keymapper.base.utils.navigation.NavDestination
+import io.github.sds100.keymapper.base.utils.navigation.handleRouteArgs
+import kotlinx.serialization.json.Json
+
+@Composable
+fun BaseMainNavHost(
+ modifier: Modifier = Modifier,
+ navController: NavHostController,
+ composableDestinations: NavGraphBuilder.() -> Unit = {},
+) {
+ NavHost(
+ modifier = modifier,
+ navController = navController,
+ startDestination = NavDestination.Home,
+ enterTransition = { slideIntoContainer(towards = AnimatedContentTransitionScope.SlideDirection.Left) },
+ exitTransition = { slideOutOfContainer(towards = AnimatedContentTransitionScope.SlideDirection.Right) },
+ popEnterTransition = { slideIntoContainer(towards = AnimatedContentTransitionScope.SlideDirection.Right) },
+ popExitTransition = { slideOutOfContainer(towards = AnimatedContentTransitionScope.SlideDirection.Right) },
+ ) {
+ composable { backStackEntry ->
+ val viewModel: InteractUiElementViewModel = hiltViewModel()
+
+ backStackEntry.handleRouteArgs { destination ->
+ destination.actionJson?.let { viewModel.loadAction(Json.decodeFromString(it)) }
+ }
+
+ InteractUiElementScreen(
+ modifier = Modifier.fillMaxSize(),
+ viewModel = viewModel,
+ )
+ }
+
+ composable {
+ val viewModel: ChooseConstraintViewModel = hiltViewModel()
+
+ ChooseConstraintScreen(
+ modifier = Modifier.fillMaxSize(),
+ viewModel = viewModel,
+ )
+ }
+
+ composableDestinations()
+ }
+}
diff --git a/base/src/main/java/io/github/sds100/keymapper/base/BaseSingletonHiltModule.kt b/base/src/main/java/io/github/sds100/keymapper/base/BaseSingletonHiltModule.kt
new file mode 100644
index 0000000000..55a0ffabfe
--- /dev/null
+++ b/base/src/main/java/io/github/sds100/keymapper/base/BaseSingletonHiltModule.kt
@@ -0,0 +1,137 @@
+package io.github.sds100.keymapper.base
+
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import io.github.sds100.keymapper.base.actions.GetActionErrorUseCase
+import io.github.sds100.keymapper.base.actions.GetActionErrorUseCaseImpl
+import io.github.sds100.keymapper.base.actions.sound.SoundsManager
+import io.github.sds100.keymapper.base.actions.sound.SoundsManagerImpl
+import io.github.sds100.keymapper.base.actions.uielement.InteractUiElementController
+import io.github.sds100.keymapper.base.actions.uielement.InteractUiElementUseCase
+import io.github.sds100.keymapper.base.backup.BackupManager
+import io.github.sds100.keymapper.base.backup.BackupManagerImpl
+import io.github.sds100.keymapper.base.constraints.GetConstraintErrorUseCase
+import io.github.sds100.keymapper.base.constraints.GetConstraintErrorUseCaseImpl
+import io.github.sds100.keymapper.base.keymaps.ConfigKeyMapUseCase
+import io.github.sds100.keymapper.base.keymaps.ConfigKeyMapUseCaseController
+import io.github.sds100.keymapper.base.keymaps.FingerprintGesturesSupportedUseCase
+import io.github.sds100.keymapper.base.keymaps.FingerprintGesturesSupportedUseCaseImpl
+import io.github.sds100.keymapper.base.keymaps.PauseKeyMapsUseCase
+import io.github.sds100.keymapper.base.keymaps.PauseKeyMapsUseCaseImpl
+import io.github.sds100.keymapper.base.onboarding.OnboardingUseCase
+import io.github.sds100.keymapper.base.onboarding.OnboardingUseCaseImpl
+import io.github.sds100.keymapper.base.system.accessibility.AccessibilityServiceAdapterImpl
+import io.github.sds100.keymapper.base.system.accessibility.ControlAccessibilityServiceUseCase
+import io.github.sds100.keymapper.base.system.accessibility.ControlAccessibilityServiceUseCaseImpl
+import io.github.sds100.keymapper.base.system.inputmethod.ShowHideInputMethodUseCase
+import io.github.sds100.keymapper.base.system.inputmethod.ShowHideInputMethodUseCaseImpl
+import io.github.sds100.keymapper.base.system.inputmethod.ShowInputMethodPickerUseCase
+import io.github.sds100.keymapper.base.system.inputmethod.ShowInputMethodPickerUseCaseImpl
+import io.github.sds100.keymapper.base.system.inputmethod.ToggleCompatibleImeUseCase
+import io.github.sds100.keymapper.base.system.inputmethod.ToggleCompatibleImeUseCaseImpl
+import io.github.sds100.keymapper.base.system.notifications.AndroidNotificationAdapter
+import io.github.sds100.keymapper.base.system.notifications.ManageNotificationsUseCase
+import io.github.sds100.keymapper.base.system.notifications.ManageNotificationsUseCaseImpl
+import io.github.sds100.keymapper.base.trigger.RecordTriggerController
+import io.github.sds100.keymapper.base.trigger.RecordTriggerUseCase
+import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider
+import io.github.sds100.keymapper.base.utils.navigation.NavigationProviderImpl
+import io.github.sds100.keymapper.base.utils.ui.DialogProvider
+import io.github.sds100.keymapper.base.utils.ui.DialogProviderImpl
+import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
+import io.github.sds100.keymapper.base.utils.ui.ResourceProviderImpl
+import io.github.sds100.keymapper.common.utils.DefaultUuidGenerator
+import io.github.sds100.keymapper.common.utils.UuidGenerator
+import io.github.sds100.keymapper.system.accessibility.AccessibilityServiceAdapter
+import io.github.sds100.keymapper.system.notifications.NotificationAdapter
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+abstract class BaseSingletonHiltModule {
+ @Singleton
+ @Binds
+ abstract fun provideNotificationAdapter(impl: AndroidNotificationAdapter): NotificationAdapter
+
+ @Singleton
+ @Binds
+ abstract fun provideAccessibilityAdapter(impl: AccessibilityServiceAdapterImpl): AccessibilityServiceAdapter
+
+ @Singleton
+ @Binds
+ abstract fun provideResourceProvider(impl: ResourceProviderImpl): ResourceProvider
+
+ @Singleton
+ @Binds
+ abstract fun provideOnboardingUseCase(impl: OnboardingUseCaseImpl): OnboardingUseCase
+
+ @Binds
+ @Singleton
+ abstract fun bindPauseKeyMapsUseCase(impl: PauseKeyMapsUseCaseImpl): PauseKeyMapsUseCase
+
+ @Binds
+ @Singleton
+ abstract fun bindShowInputMethodPickerUseCase(impl: ShowInputMethodPickerUseCaseImpl): ShowInputMethodPickerUseCase
+
+ @Binds
+ @Singleton
+ abstract fun bindControlAccessibilityServiceUseCase(impl: ControlAccessibilityServiceUseCaseImpl): ControlAccessibilityServiceUseCase
+
+ @Binds
+ @Singleton
+ abstract fun bindToggleCompatibleImeUseCase(impl: ToggleCompatibleImeUseCaseImpl): ToggleCompatibleImeUseCase
+
+ @Binds
+ @Singleton
+ abstract fun bindInteractUiElementUseCase(impl: InteractUiElementController): InteractUiElementUseCase
+
+ @Binds
+ @Singleton
+ abstract fun bindShowHideInputMethodUseCase(impl: ShowHideInputMethodUseCaseImpl): ShowHideInputMethodUseCase
+
+ @Binds
+ @Singleton
+ abstract fun bindBackupManager(impl: BackupManagerImpl): BackupManager
+
+ @Binds
+ @Singleton
+ abstract fun bindSoundsManager(impl: SoundsManagerImpl): SoundsManager
+
+ @Binds
+ @Singleton
+ abstract fun bindConfigKeyMapUseCase(impl: ConfigKeyMapUseCaseController): ConfigKeyMapUseCase
+
+ @Binds
+ @Singleton
+ abstract fun bindRecordTriggerUseCase(impl: RecordTriggerController): RecordTriggerUseCase
+
+ @Binds
+ @Singleton
+ abstract fun bindFingerprintGesturesSupportedUseCase(impl: FingerprintGesturesSupportedUseCaseImpl): FingerprintGesturesSupportedUseCase
+
+ @Binds
+ @Singleton
+ abstract fun bindGetActionErrorUseCase(impl: GetActionErrorUseCaseImpl): GetActionErrorUseCase
+
+ @Binds
+ @Singleton
+ abstract fun bindGetConstraintErrorUseCase(impl: GetConstraintErrorUseCaseImpl): GetConstraintErrorUseCase
+
+ @Binds
+ @Singleton
+ abstract fun bindManageNotificationsUseCase(impl: ManageNotificationsUseCaseImpl): ManageNotificationsUseCase
+
+ @Binds
+ @Singleton
+ abstract fun bindUuidGenerator(impl: DefaultUuidGenerator): UuidGenerator
+
+ @Binds
+ @Singleton
+ abstract fun bindNavigationProvider(impl: NavigationProviderImpl): NavigationProvider
+
+ @Binds
+ @Singleton
+ abstract fun bindDialogProvider(impl: DialogProviderImpl): DialogProvider
+}
diff --git a/base/src/main/java/io/github/sds100/keymapper/base/BaseViewModelHiltModule.kt b/base/src/main/java/io/github/sds100/keymapper/base/BaseViewModelHiltModule.kt
new file mode 100644
index 0000000000..526b59f6e7
--- /dev/null
+++ b/base/src/main/java/io/github/sds100/keymapper/base/BaseViewModelHiltModule.kt
@@ -0,0 +1,113 @@
+package io.github.sds100.keymapper.base
+
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ViewModelComponent
+import dagger.hilt.android.scopes.ViewModelScoped
+import io.github.sds100.keymapper.base.actions.CreateActionUseCase
+import io.github.sds100.keymapper.base.actions.CreateActionUseCaseImpl
+import io.github.sds100.keymapper.base.actions.TestActionUseCase
+import io.github.sds100.keymapper.base.actions.TestActionUseCaseImpl
+import io.github.sds100.keymapper.base.actions.keyevent.ConfigKeyEventUseCase
+import io.github.sds100.keymapper.base.actions.keyevent.ConfigKeyEventUseCaseImpl
+import io.github.sds100.keymapper.base.actions.sound.ChooseSoundFileUseCase
+import io.github.sds100.keymapper.base.actions.sound.ChooseSoundFileUseCaseImpl
+import io.github.sds100.keymapper.base.backup.BackupRestoreMappingsUseCase
+import io.github.sds100.keymapper.base.backup.BackupRestoreMappingsUseCaseImpl
+import io.github.sds100.keymapper.base.constraints.CreateConstraintUseCase
+import io.github.sds100.keymapper.base.constraints.CreateConstraintUseCaseImpl
+import io.github.sds100.keymapper.base.home.ShowHomeScreenAlertsUseCase
+import io.github.sds100.keymapper.base.home.ShowHomeScreenAlertsUseCaseImpl
+import io.github.sds100.keymapper.base.keymaps.CreateKeyMapShortcutUseCase
+import io.github.sds100.keymapper.base.keymaps.CreateKeyMapShortcutUseCaseImpl
+import io.github.sds100.keymapper.base.keymaps.DisplayKeyMapUseCase
+import io.github.sds100.keymapper.base.keymaps.DisplayKeyMapUseCaseImpl
+import io.github.sds100.keymapper.base.keymaps.ListKeyMapsUseCase
+import io.github.sds100.keymapper.base.keymaps.ListKeyMapsUseCaseImpl
+import io.github.sds100.keymapper.base.logging.DisplayLogUseCase
+import io.github.sds100.keymapper.base.logging.DisplayLogUseCaseImpl
+import io.github.sds100.keymapper.base.settings.ConfigSettingsUseCase
+import io.github.sds100.keymapper.base.settings.ConfigSettingsUseCaseImpl
+import io.github.sds100.keymapper.base.sorting.SortKeyMapsUseCase
+import io.github.sds100.keymapper.base.sorting.SortKeyMapsUseCaseImpl
+import io.github.sds100.keymapper.base.system.apps.DisplayAppShortcutsUseCase
+import io.github.sds100.keymapper.base.system.apps.DisplayAppShortcutsUseCaseImpl
+import io.github.sds100.keymapper.base.system.apps.DisplayAppsUseCase
+import io.github.sds100.keymapper.base.system.apps.DisplayAppsUseCaseImpl
+import io.github.sds100.keymapper.base.system.bluetooth.ChooseBluetoothDeviceUseCase
+import io.github.sds100.keymapper.base.system.bluetooth.ChooseBluetoothDeviceUseCaseImpl
+import io.github.sds100.keymapper.base.trigger.SetupGuiKeyboardUseCase
+import io.github.sds100.keymapper.base.trigger.SetupGuiKeyboardUseCaseImpl
+
+@Module
+@InstallIn(ViewModelComponent::class)
+abstract class BaseViewModelHiltModule {
+ @Binds
+ @ViewModelScoped
+ abstract fun bindDisplayKeyMapUseCase(impl: DisplayKeyMapUseCaseImpl): DisplayKeyMapUseCase
+
+ @Binds
+ @ViewModelScoped
+ abstract fun bindListKeyMapsUseCase(impl: ListKeyMapsUseCaseImpl): ListKeyMapsUseCase
+
+ @Binds
+ @ViewModelScoped
+ abstract fun bindBackupRestoreMappingsUseCase(impl: BackupRestoreMappingsUseCaseImpl): BackupRestoreMappingsUseCase
+
+ @Binds
+ @ViewModelScoped
+ abstract fun bindShowHomeScreenAlertsUseCase(impl: ShowHomeScreenAlertsUseCaseImpl): ShowHomeScreenAlertsUseCase
+
+ @Binds
+ @ViewModelScoped
+ abstract fun bindSortKeyMapsUseCase(impl: SortKeyMapsUseCaseImpl): SortKeyMapsUseCase
+
+ @Binds
+ @ViewModelScoped
+ abstract fun bindDisplayLogUseCase(impl: DisplayLogUseCaseImpl): DisplayLogUseCase
+
+ @Binds
+ @ViewModelScoped
+ abstract fun bindConfigSettingsUseCase(impl: ConfigSettingsUseCaseImpl): ConfigSettingsUseCase
+
+ @Binds
+ @ViewModelScoped
+ abstract fun bindChooseBluetoothDeviceUseCase(impl: ChooseBluetoothDeviceUseCaseImpl): ChooseBluetoothDeviceUseCase
+
+ @Binds
+ @ViewModelScoped
+ abstract fun bindChooseSoundFileUseCase(impl: ChooseSoundFileUseCaseImpl): ChooseSoundFileUseCase
+
+ @Binds
+ @ViewModelScoped
+ abstract fun bindConfigKeyEventUseCase(impl: ConfigKeyEventUseCaseImpl): ConfigKeyEventUseCase
+
+ @Binds
+ @ViewModelScoped
+ abstract fun bindDisplayAppShortcutsUseCase(impl: DisplayAppShortcutsUseCaseImpl): DisplayAppShortcutsUseCase
+
+ @Binds
+ @ViewModelScoped
+ abstract fun bindDisplayAppsUseCase(impl: DisplayAppsUseCaseImpl): DisplayAppsUseCase
+
+ @Binds
+ @ViewModelScoped
+ abstract fun bindTestActionUseCase(impl: TestActionUseCaseImpl): TestActionUseCase
+
+ @Binds
+ @ViewModelScoped
+ abstract fun bindCreateKeyMapShortcutUseCase(impl: CreateKeyMapShortcutUseCaseImpl): CreateKeyMapShortcutUseCase
+
+ @Binds
+ @ViewModelScoped
+ abstract fun bindSetupGuiKeyboardUseCase(impl: SetupGuiKeyboardUseCaseImpl): SetupGuiKeyboardUseCase
+
+ @Binds
+ @ViewModelScoped
+ abstract fun bindCreateActionUseCase(impl: CreateActionUseCaseImpl): CreateActionUseCase
+
+ @Binds
+ @ViewModelScoped
+ abstract fun bindCreateConstraintUseCase(impl: CreateConstraintUseCaseImpl): CreateConstraintUseCase
+}
diff --git a/app/src/main/java/io/github/sds100/keymapper/system/BootBroadcastReceiver.kt b/base/src/main/java/io/github/sds100/keymapper/base/BootBroadcastReceiver.kt
similarity index 61%
rename from app/src/main/java/io/github/sds100/keymapper/system/BootBroadcastReceiver.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/BootBroadcastReceiver.kt
index c61aab628b..247b5282bc 100644
--- a/app/src/main/java/io/github/sds100/keymapper/system/BootBroadcastReceiver.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/BootBroadcastReceiver.kt
@@ -1,20 +1,15 @@
-package io.github.sds100.keymapper.system
+package io.github.sds100.keymapper.base
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
-import io.github.sds100.keymapper.KeyMapperApp
-
-/**
- * Created by sds100 on 24/03/2019.
- */
class BootBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
context ?: return
if (intent?.action == Intent.ACTION_LOCKED_BOOT_COMPLETED) {
- (context.applicationContext as? KeyMapperApp)?.onBootUnlocked()
+ (context.applicationContext as? BaseKeyMapperApp)?.onBootUnlocked()
}
}
}
diff --git a/base/src/main/java/io/github/sds100/keymapper/base/Constants.kt b/base/src/main/java/io/github/sds100/keymapper/base/Constants.kt
new file mode 100644
index 0000000000..d5f8c15086
--- /dev/null
+++ b/base/src/main/java/io/github/sds100/keymapper/base/Constants.kt
@@ -0,0 +1,8 @@
+package io.github.sds100.keymapper.base
+
+import android.os.Build
+
+object Constants {
+ const val MIN_API: Int = Build.VERSION_CODES.LOLLIPOP
+ const val MAX_API: Int = 1000
+}
diff --git a/app/src/main/java/io/github/sds100/keymapper/about/AboutFragment.kt b/base/src/main/java/io/github/sds100/keymapper/base/about/AboutFragment.kt
similarity index 81%
rename from app/src/main/java/io/github/sds100/keymapper/about/AboutFragment.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/about/AboutFragment.kt
index 7c994503ae..cabec8079b 100644
--- a/app/src/main/java/io/github/sds100/keymapper/about/AboutFragment.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/about/AboutFragment.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.about
+package io.github.sds100.keymapper.base.about
import android.os.Bundle
import android.view.LayoutInflater
@@ -10,15 +10,17 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
-import io.github.sds100.keymapper.Constants
-import io.github.sds100.keymapper.databinding.FragmentAboutBinding
-
-/**
- * Created by sds100 on 05/04/2020.
- */
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.sds100.keymapper.base.databinding.FragmentAboutBinding
+import io.github.sds100.keymapper.common.BuildConfigProvider
+import javax.inject.Inject
+@AndroidEntryPoint
class AboutFragment : Fragment() {
+ @Inject
+ lateinit var buildConfigProvider: BuildConfigProvider
+
/**
* Scoped to the lifecycle of the fragment's view (between onCreateView and onDestroyView)
*/
@@ -57,7 +59,7 @@ class AboutFragment : Fragment() {
onBackPressed()
}
- version = "${Constants.VERSION} ${Constants.VERSION_CODE}"
+ version = "${buildConfigProvider.version} ${buildConfigProvider.versionCode}"
}
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/Action.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/Action.kt
similarity index 94%
rename from app/src/main/java/io/github/sds100/keymapper/actions/Action.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/Action.kt
index e8e1050ea7..ca071be8d2 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/Action.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/Action.kt
@@ -1,21 +1,17 @@
-package io.github.sds100.keymapper.actions
-
+package io.github.sds100.keymapper.base.actions
+
+import io.github.sds100.keymapper.base.keymaps.KeyMap
+import io.github.sds100.keymapper.common.utils.hasFlag
+import io.github.sds100.keymapper.common.utils.success
+import io.github.sds100.keymapper.common.utils.then
+import io.github.sds100.keymapper.common.utils.valueOrNull
+import io.github.sds100.keymapper.common.utils.withFlag
import io.github.sds100.keymapper.data.entities.ActionEntity
import io.github.sds100.keymapper.data.entities.EntityExtra
import io.github.sds100.keymapper.data.entities.getData
-import io.github.sds100.keymapper.keymaps.KeyMap
-import io.github.sds100.keymapper.util.success
-import io.github.sds100.keymapper.util.then
-import io.github.sds100.keymapper.util.valueOrNull
import kotlinx.serialization.Serializable
-import splitties.bitflags.hasFlag
-import splitties.bitflags.withFlag
import java.util.UUID
-/**
- * Created by sds100 on 09/03/2021.
- */
-
@Serializable
data class Action(
val uid: String = UUID.randomUUID().toString(),
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/ActionCategory.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionCategory.kt
similarity index 72%
rename from app/src/main/java/io/github/sds100/keymapper/actions/ActionCategory.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/ActionCategory.kt
index c6dfed43a6..55bf05e5d3 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/ActionCategory.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionCategory.kt
@@ -1,8 +1,5 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
-/**
- * Created by sds100 on 23/03/2021.
- */
enum class ActionCategory {
APPS,
INPUT,
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/ActionData.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionData.kt
similarity index 99%
rename from app/src/main/java/io/github/sds100/keymapper/actions/ActionData.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/ActionData.kt
index 7889ee4f2a..37437eccb4 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/ActionData.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionData.kt
@@ -1,9 +1,9 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
-import io.github.sds100.keymapper.actions.pinchscreen.PinchScreenType
-import io.github.sds100.keymapper.actions.uielement.NodeInteractionType
+import io.github.sds100.keymapper.common.utils.NodeInteractionType
+import io.github.sds100.keymapper.common.utils.Orientation
+import io.github.sds100.keymapper.common.utils.PinchScreenType
import io.github.sds100.keymapper.system.camera.CameraLens
-import io.github.sds100.keymapper.system.display.Orientation
import io.github.sds100.keymapper.system.intents.IntentExtraModel
import io.github.sds100.keymapper.system.intents.IntentTarget
import io.github.sds100.keymapper.system.network.HttpMethod
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/ActionDataEntityMapper.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionDataEntityMapper.kt
similarity index 98%
rename from app/src/main/java/io/github/sds100/keymapper/actions/ActionDataEntityMapper.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/ActionDataEntityMapper.kt
index b56a2856ee..3ea9be3414 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/ActionDataEntityMapper.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionDataEntityMapper.kt
@@ -1,8 +1,16 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
import androidx.core.net.toUri
-import io.github.sds100.keymapper.actions.pinchscreen.PinchScreenType
-import io.github.sds100.keymapper.actions.uielement.NodeInteractionType
+import io.github.sds100.keymapper.common.utils.KMError
+import io.github.sds100.keymapper.common.utils.KMResult
+import io.github.sds100.keymapper.common.utils.NodeInteractionType
+import io.github.sds100.keymapper.common.utils.PinchScreenType
+import io.github.sds100.keymapper.common.utils.Success
+import io.github.sds100.keymapper.common.utils.getKey
+import io.github.sds100.keymapper.common.utils.hasFlag
+import io.github.sds100.keymapper.common.utils.success
+import io.github.sds100.keymapper.common.utils.then
+import io.github.sds100.keymapper.common.utils.valueOrNull
import io.github.sds100.keymapper.data.db.typeconverter.ConstantTypeConverters
import io.github.sds100.keymapper.data.db.typeconverter.NodeInteractionTypeSetTypeConverter
import io.github.sds100.keymapper.data.entities.ActionEntity
@@ -15,19 +23,7 @@ import io.github.sds100.keymapper.system.network.HttpMethod
import io.github.sds100.keymapper.system.volume.DndMode
import io.github.sds100.keymapper.system.volume.RingerMode
import io.github.sds100.keymapper.system.volume.VolumeStream
-import io.github.sds100.keymapper.util.Error
-import io.github.sds100.keymapper.util.Result
-import io.github.sds100.keymapper.util.Success
-import io.github.sds100.keymapper.util.getKey
-import io.github.sds100.keymapper.util.success
-import io.github.sds100.keymapper.util.then
-import io.github.sds100.keymapper.util.valueOrNull
import kotlinx.serialization.json.Json
-import splitties.bitflags.hasFlag
-
-/**
- * Created by sds100 on 13/03/2021.
- */
object ActionDataEntityMapper {
@@ -613,10 +609,10 @@ object ActionDataEntityMapper {
}
}
- private fun convertNodeInteractionType(string: String): Result = try {
+ private fun convertNodeInteractionType(string: String): KMResult = try {
Success(NodeInteractionType.valueOf(string))
} catch (e: IllegalArgumentException) {
- Error.Exception(e)
+ KMError.Exception(e)
}
fun toEntity(data: ActionData): ActionEntity {
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/ActionErrorSnapshot.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionErrorSnapshot.kt
similarity index 76%
rename from app/src/main/java/io/github/sds100/keymapper/actions/ActionErrorSnapshot.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/ActionErrorSnapshot.kt
index 11c4129b8c..a70eaa68b5 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/ActionErrorSnapshot.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionErrorSnapshot.kt
@@ -1,19 +1,21 @@
-package io.github.sds100.keymapper.actions
-
-import io.github.sds100.keymapper.actions.sound.SoundsManager
-import io.github.sds100.keymapper.shizuku.ShizukuAdapter
+package io.github.sds100.keymapper.base.actions
+
+import io.github.sds100.keymapper.base.actions.sound.SoundsManager
+import io.github.sds100.keymapper.base.system.inputmethod.KeyMapperImeHelper
+import io.github.sds100.keymapper.common.BuildConfigProvider
+import io.github.sds100.keymapper.common.utils.KMError
+import io.github.sds100.keymapper.common.utils.onFailure
+import io.github.sds100.keymapper.common.utils.onSuccess
+import io.github.sds100.keymapper.system.SystemError
import io.github.sds100.keymapper.system.apps.PackageManagerAdapter
import io.github.sds100.keymapper.system.camera.CameraAdapter
import io.github.sds100.keymapper.system.camera.CameraLens
import io.github.sds100.keymapper.system.inputmethod.InputMethodAdapter
-import io.github.sds100.keymapper.system.inputmethod.KeyMapperImeHelper
import io.github.sds100.keymapper.system.permissions.Permission
import io.github.sds100.keymapper.system.permissions.PermissionAdapter
import io.github.sds100.keymapper.system.permissions.SystemFeatureAdapter
import io.github.sds100.keymapper.system.ringtones.RingtoneAdapter
-import io.github.sds100.keymapper.util.Error
-import io.github.sds100.keymapper.util.onFailure
-import io.github.sds100.keymapper.util.onSuccess
+import io.github.sds100.keymapper.system.shizuku.ShizukuAdapter
class LazyActionErrorSnapshot(
private val packageManager: PackageManagerAdapter,
@@ -24,13 +26,14 @@ class LazyActionErrorSnapshot(
private val soundsManager: SoundsManager,
shizukuAdapter: ShizukuAdapter,
private val ringtoneAdapter: RingtoneAdapter,
+ buildConfigProvider: BuildConfigProvider,
) : ActionErrorSnapshot,
IsActionSupportedUseCase by IsActionSupportedUseCaseImpl(
systemFeatureAdapter,
cameraAdapter,
permissionAdapter,
) {
- private val keyMapperImeHelper = KeyMapperImeHelper(inputMethodAdapter)
+ private val keyMapperImeHelper = KeyMapperImeHelper(inputMethodAdapter, buildConfigProvider.packageName)
private val isCompatibleImeEnabled by lazy { keyMapperImeHelper.isCompatibleImeEnabled() }
private val isCompatibleImeChosen by lazy { keyMapperImeHelper.isCompatibleImeChosen() }
@@ -50,7 +53,7 @@ class LazyActionErrorSnapshot(
}
}
- override fun getError(action: ActionData): Error? {
+ override fun getError(action: ActionData): KMError? {
val isSupportedError = isSupported(action.id)
if (isSupportedError != null) {
@@ -60,26 +63,26 @@ class LazyActionErrorSnapshot(
if (action.canUseShizukuToPerform() && isShizukuInstalled) {
if (!(action.canUseImeToPerform() && isCompatibleImeChosen)) {
when {
- !isShizukuStarted -> return Error.ShizukuNotStarted
+ !isShizukuStarted -> return KMError.ShizukuNotStarted
- !isPermissionGranted(Permission.SHIZUKU) -> return Error.PermissionDenied(
+ !isPermissionGranted(Permission.SHIZUKU) -> return SystemError.PermissionDenied(
Permission.SHIZUKU,
)
}
}
} else if (action.canUseImeToPerform()) {
if (!isCompatibleImeEnabled) {
- return Error.NoCompatibleImeEnabled
+ return KMError.NoCompatibleImeEnabled
}
if (!isCompatibleImeChosen) {
- return Error.NoCompatibleImeChosen
+ return KMError.NoCompatibleImeChosen
}
}
ActionUtils.getRequiredPermissions(action.id).forEach { permission ->
if (!isPermissionGranted(permission)) {
- return Error.PermissionDenied(permission)
+ return SystemError.PermissionDenied(permission)
}
}
@@ -98,7 +101,7 @@ class LazyActionErrorSnapshot(
if (
action.useShell && !isPermissionGranted(Permission.ROOT)
) {
- return Error.PermissionDenied(Permission.ROOT)
+ return SystemError.PermissionDenied(Permission.ROOT)
}
is ActionData.Sound.SoundFile -> {
@@ -109,21 +112,21 @@ class LazyActionErrorSnapshot(
is ActionData.Sound.Ringtone -> {
if (!ringtoneAdapter.exists(action.uri)) {
- return Error.CantFindSoundFile
+ return KMError.CantFindSoundFile
}
}
is ActionData.VoiceAssistant -> {
if (!isVoiceAssistantInstalled) {
- return Error.NoVoiceAssistant
+ return KMError.NoVoiceAssistant
}
}
is ActionData.Flashlight ->
if (!flashLenses.contains(action.lens)) {
return when (action.lens) {
- CameraLens.FRONT -> Error.FrontFlashNotFound
- CameraLens.BACK -> Error.BackFlashNotFound
+ CameraLens.FRONT -> KMError.FrontFlashNotFound
+ CameraLens.BACK -> KMError.BackFlashNotFound
}
}
@@ -138,15 +141,15 @@ class LazyActionErrorSnapshot(
return null
}
- private fun getAppError(packageName: String): Error? {
+ private fun getAppError(packageName: String): KMError? {
packageManager.isAppEnabled(packageName).onSuccess { isEnabled ->
if (!isEnabled) {
- return Error.AppDisabled(packageName)
+ return KMError.AppDisabled(packageName)
}
}
if (!packageManager.isAppInstalled(packageName)) {
- return Error.AppNotFound(packageName)
+ return KMError.AppNotFound(packageName)
}
return null
@@ -164,5 +167,5 @@ class LazyActionErrorSnapshot(
}
interface ActionErrorSnapshot {
- fun getError(action: ActionData): Error?
+ fun getError(action: ActionData): KMError?
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/ActionId.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionId.kt
similarity index 96%
rename from app/src/main/java/io/github/sds100/keymapper/actions/ActionId.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/ActionId.kt
index 80840e9949..b8034aa84b 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/ActionId.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionId.kt
@@ -1,8 +1,5 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
-/**
- * Created by sds100 on 15/03/2021.
- */
enum class ActionId {
APP,
APP_SHORTCUT,
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/ActionListItem.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionListItem.kt
similarity index 97%
rename from app/src/main/java/io/github/sds100/keymapper/actions/ActionListItem.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/ActionListItem.kt
index 92276cfeb8..f842381aae 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/ActionListItem.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionListItem.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.draggable
@@ -43,11 +43,11 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.google.accompanist.drawablepainter.rememberDrawablePainter
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.util.drawable
-import io.github.sds100.keymapper.util.ui.LinkType
-import io.github.sds100.keymapper.util.ui.compose.ComposeIconInfo
-import io.github.sds100.keymapper.util.ui.compose.DragDropState
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.utils.ui.LinkType
+import io.github.sds100.keymapper.base.utils.ui.compose.ComposeIconInfo
+import io.github.sds100.keymapper.base.utils.ui.compose.DragDropState
+import io.github.sds100.keymapper.base.utils.ui.drawable
@Composable
fun ActionListItem(
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/ActionOptionsBottomSheet.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionOptionsBottomSheet.kt
similarity index 96%
rename from app/src/main/java/io/github/sds100/keymapper/actions/ActionOptionsBottomSheet.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/ActionOptionsBottomSheet.kt
index 4a8de35bf8..fac1d4ae7c 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/ActionOptionsBottomSheet.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionOptionsBottomSheet.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -37,15 +37,15 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.compose.KeyMapperTheme
-import io.github.sds100.keymapper.util.ui.SliderMaximums
-import io.github.sds100.keymapper.util.ui.SliderMinimums
-import io.github.sds100.keymapper.util.ui.SliderStepSizes
-import io.github.sds100.keymapper.util.ui.compose.CheckBoxText
-import io.github.sds100.keymapper.util.ui.compose.RadioButtonText
-import io.github.sds100.keymapper.util.ui.compose.SliderOptionText
-import io.github.sds100.keymapper.util.ui.compose.openUriSafe
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.compose.KeyMapperTheme
+import io.github.sds100.keymapper.base.utils.ui.SliderMaximums
+import io.github.sds100.keymapper.base.utils.ui.SliderMinimums
+import io.github.sds100.keymapper.base.utils.ui.SliderStepSizes
+import io.github.sds100.keymapper.base.utils.ui.compose.CheckBoxText
+import io.github.sds100.keymapper.base.utils.ui.compose.RadioButtonText
+import io.github.sds100.keymapper.base.utils.ui.compose.SliderOptionText
+import io.github.sds100.keymapper.base.utils.ui.compose.openUriSafe
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/ActionUiHelper.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionUiHelper.kt
similarity index 93%
rename from app/src/main/java/io/github/sds100/keymapper/actions/ActionUiHelper.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/ActionUiHelper.kt
index 723084996b..b9a0b61f5e 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/ActionUiHelper.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionUiHelper.kt
@@ -1,27 +1,27 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
import android.os.Build
import android.view.KeyEvent
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Android
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.actions.pinchscreen.PinchScreenType
-import io.github.sds100.keymapper.keymaps.KeyMap
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.keymaps.KeyMap
+import io.github.sds100.keymapper.base.utils.DndModeStrings
+import io.github.sds100.keymapper.base.utils.InputEventStrings
+import io.github.sds100.keymapper.base.utils.RingerModeStrings
+import io.github.sds100.keymapper.base.utils.VolumeStreamStrings
+import io.github.sds100.keymapper.base.utils.ui.IconInfo
+import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
+import io.github.sds100.keymapper.base.utils.ui.TintType
+import io.github.sds100.keymapper.base.utils.ui.compose.ComposeIconInfo
+import io.github.sds100.keymapper.common.utils.Orientation
+import io.github.sds100.keymapper.common.utils.PinchScreenType
+import io.github.sds100.keymapper.common.utils.handle
+import io.github.sds100.keymapper.common.utils.hasFlag
+import io.github.sds100.keymapper.common.utils.toPercentString
import io.github.sds100.keymapper.system.camera.CameraLens
import io.github.sds100.keymapper.system.devices.InputDeviceUtils
-import io.github.sds100.keymapper.system.display.OrientationUtils
-import io.github.sds100.keymapper.system.inputevents.InputEventUtils
import io.github.sds100.keymapper.system.intents.IntentTarget
-import io.github.sds100.keymapper.system.volume.DndModeUtils
-import io.github.sds100.keymapper.system.volume.RingerModeUtils
-import io.github.sds100.keymapper.system.volume.VolumeStreamUtils
-import io.github.sds100.keymapper.util.handle
-import io.github.sds100.keymapper.util.toPercentString
-import io.github.sds100.keymapper.util.ui.IconInfo
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-import io.github.sds100.keymapper.util.ui.TintType
-import io.github.sds100.keymapper.util.ui.compose.ComposeIconInfo
-import splitties.bitflags.hasFlag
class ActionUiHelper(
displayActionUseCase: DisplayActionUseCase,
@@ -50,9 +50,9 @@ class ActionUiHelper(
getString(R.string.description_keyevent_through_shell, keyCodeString)
} else {
val metaStateString = buildString {
- InputEventUtils.MODIFIER_LABELS.entries.forEach {
- val modifier = it.key
- val labelRes = it.value
+ for (label in InputEventStrings.MODIFIER_LABELS.entries) {
+ val modifier = label.key
+ val labelRes = label.value
if (action.metaState.hasFlag(modifier)) {
append("${getString(labelRes)} + ")
@@ -90,7 +90,7 @@ class ActionUiHelper(
}
is ActionData.DoNotDisturb.Enable -> {
- val dndModeString = getString(DndModeUtils.getLabel(action.dndMode))
+ val dndModeString = getString(DndModeStrings.getLabel(action.dndMode))
getString(
R.string.action_enable_dnd_mode_formatted,
dndModeString,
@@ -98,7 +98,7 @@ class ActionUiHelper(
}
is ActionData.DoNotDisturb.Toggle -> {
- val dndModeString = getString(DndModeUtils.getLabel(action.dndMode))
+ val dndModeString = getString(DndModeStrings.getLabel(action.dndMode))
getString(
R.string.action_toggle_dnd_mode_formatted,
dndModeString,
@@ -108,7 +108,7 @@ class ActionUiHelper(
ActionData.DoNotDisturb.Disable -> getString(R.string.action_disable_dnd_mode)
is ActionData.Volume.SetRingerMode -> {
- val ringerModeString = getString(RingerModeUtils.getLabel(action.ringerMode))
+ val ringerModeString = getString(RingerModeStrings.getLabel(action.ringerMode))
getString(R.string.action_change_ringer_mode_formatted, ringerModeString)
}
@@ -120,7 +120,7 @@ class ActionUiHelper(
when (action) {
is ActionData.Volume.Stream -> {
val streamString = getString(
- VolumeStreamUtils.getLabel(action.volumeStream),
+ VolumeStreamStrings.getLabel(action.volumeStream),
)
if (action.showVolumeUi) {
@@ -190,7 +190,7 @@ class ActionUiHelper(
is ActionData.Volume.SetRingerMode -> {
val ringerModeString =
- getString(RingerModeUtils.getLabel(action.ringerMode))
+ getString(RingerModeStrings.getLabel(action.ringerMode))
string = getString(
R.string.action_change_ringer_mode_formatted,
@@ -502,8 +502,13 @@ class ActionUiHelper(
ActionData.OpenSettings -> getString(R.string.action_open_settings)
is ActionData.Rotation.CycleRotations -> {
- val orientationStrings = action.orientations.map {
- getString(OrientationUtils.getLabel(it))
+ val orientationStrings = action.orientations.map { orientation ->
+ when (orientation) {
+ Orientation.ORIENTATION_0 -> R.string.orientation_0
+ Orientation.ORIENTATION_90 -> R.string.orientation_90
+ Orientation.ORIENTATION_180 -> R.string.orientation_180
+ Orientation.ORIENTATION_270 -> R.string.orientation_270
+ }
}
getString(
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/ActionUtils.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionUtils.kt
similarity index 98%
rename from app/src/main/java/io/github/sds100/keymapper/actions/ActionUtils.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/ActionUtils.kt
index f7833f70f0..638274e2d5 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/ActionUtils.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionUtils.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
import android.content.pm.PackageManager
import android.os.Build
@@ -71,22 +71,18 @@ import androidx.compose.material.icons.rounded.ContentPaste
import androidx.compose.material.icons.rounded.Wifi
import androidx.compose.material.icons.rounded.WifiOff
import androidx.compose.ui.graphics.vector.ImageVector
-import io.github.sds100.keymapper.Constants
-import io.github.sds100.keymapper.R
+import io.github.sds100.keymapper.base.Constants
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.utils.ui.compose.icons.HomeIotDevice
+import io.github.sds100.keymapper.base.utils.ui.compose.icons.InstantMix
+import io.github.sds100.keymapper.base.utils.ui.compose.icons.JumpToElement
+import io.github.sds100.keymapper.base.utils.ui.compose.icons.KeyMapperIcons
+import io.github.sds100.keymapper.base.utils.ui.compose.icons.MatchWord
+import io.github.sds100.keymapper.base.utils.ui.compose.icons.NfcOff
+import io.github.sds100.keymapper.base.utils.ui.compose.icons.TextSelectEnd
+import io.github.sds100.keymapper.base.utils.ui.compose.icons.TopPanelClose
+import io.github.sds100.keymapper.base.utils.ui.compose.icons.TopPanelOpen
import io.github.sds100.keymapper.system.permissions.Permission
-import io.github.sds100.keymapper.util.ui.compose.icons.HomeIotDevice
-import io.github.sds100.keymapper.util.ui.compose.icons.InstantMix
-import io.github.sds100.keymapper.util.ui.compose.icons.JumpToElement
-import io.github.sds100.keymapper.util.ui.compose.icons.KeyMapperIcons
-import io.github.sds100.keymapper.util.ui.compose.icons.MatchWord
-import io.github.sds100.keymapper.util.ui.compose.icons.NfcOff
-import io.github.sds100.keymapper.util.ui.compose.icons.TextSelectEnd
-import io.github.sds100.keymapper.util.ui.compose.icons.TopPanelClose
-import io.github.sds100.keymapper.util.ui.compose.icons.TopPanelOpen
-
-/**
- * Created by sds100 on 16/03/2021.
- */
object ActionUtils {
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/ActionsScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionsScreen.kt
similarity index 93%
rename from app/src/main/java/io/github/sds100/keymapper/actions/ActionsScreen.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/ActionsScreen.kt
index e53596658e..14eadb9da1 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/ActionsScreen.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionsScreen.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -37,16 +37,16 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.compose.KeyMapperTheme
-import io.github.sds100.keymapper.keymaps.ShortcutModel
-import io.github.sds100.keymapper.keymaps.ShortcutRow
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.compose.KeyMapperTheme
+import io.github.sds100.keymapper.base.keymaps.ShortcutModel
+import io.github.sds100.keymapper.base.keymaps.ShortcutRow
+import io.github.sds100.keymapper.base.utils.ui.LinkType
+import io.github.sds100.keymapper.base.utils.ui.compose.ComposeIconInfo
+import io.github.sds100.keymapper.base.utils.ui.compose.DraggableItem
+import io.github.sds100.keymapper.base.utils.ui.compose.rememberDragDropState
+import io.github.sds100.keymapper.common.utils.State
import io.github.sds100.keymapper.system.camera.CameraLens
-import io.github.sds100.keymapper.util.State
-import io.github.sds100.keymapper.util.ui.LinkType
-import io.github.sds100.keymapper.util.ui.compose.ComposeIconInfo
-import io.github.sds100.keymapper.util.ui.compose.DraggableItem
-import io.github.sds100.keymapper.util.ui.compose.rememberDragDropState
import kotlinx.coroutines.flow.update
@OptIn(ExperimentalMaterial3Api::class)
@@ -125,7 +125,7 @@ private fun ActionsScreen(
State.Loading -> Loading()
is State.Data -> Surface(modifier = modifier) {
Column {
- when (state.data) {
+ when (val data = state.data) {
is ConfigActionsState.Empty -> {
Column(
modifier = Modifier.weight(1f),
@@ -140,7 +140,7 @@ private fun ActionsScreen(
textAlign = TextAlign.Center,
)
- if (state.data.shortcuts.isNotEmpty()) {
+ if (data.shortcuts.isNotEmpty()) {
Text(
text = stringResource(R.string.recently_used_actions),
style = MaterialTheme.typography.titleSmall,
@@ -152,7 +152,7 @@ private fun ActionsScreen(
modifier = Modifier
.padding(horizontal = 32.dp)
.fillMaxWidth(),
- shortcuts = state.data.shortcuts,
+ shortcuts = data.shortcuts,
onClick = onClickShortcut,
)
}
@@ -162,7 +162,7 @@ private fun ActionsScreen(
is ConfigActionsState.Loaded -> {
Spacer(Modifier.height(8.dp))
- if (state.data.actions.isNotEmpty()) {
+ if (data.actions.isNotEmpty()) {
Spacer(Modifier.height(8.dp))
Text(
@@ -176,9 +176,9 @@ private fun ActionsScreen(
ActionList(
modifier = Modifier.weight(1f),
- actionList = state.data.actions,
- shortcuts = state.data.shortcuts,
- isReorderingEnabled = state.data.isReorderingEnabled,
+ actionList = data.actions,
+ shortcuts = data.shortcuts,
+ isReorderingEnabled = data.isReorderingEnabled,
onRemoveClick = {
actionToDelete = it
showDeleteDialog = true
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/ChooseActionScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ChooseActionScreen.kt
similarity index 93%
rename from app/src/main/java/io/github/sds100/keymapper/actions/ChooseActionScreen.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/ChooseActionScreen.kt
index 34cc3a8616..fce1f2f42c 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/ChooseActionScreen.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ChooseActionScreen.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -20,7 +20,6 @@ import androidx.compose.material.icons.rounded.Bluetooth
import androidx.compose.material.icons.rounded.Wifi
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.CircularProgressIndicator
-import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
@@ -38,22 +37,21 @@ import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.compose.KeyMapperTheme
-import io.github.sds100.keymapper.util.State
-import io.github.sds100.keymapper.util.ui.compose.ComposeIconInfo
-import io.github.sds100.keymapper.util.ui.compose.SearchAppBarActions
-import io.github.sds100.keymapper.util.ui.compose.SimpleListItemFixedHeight
-import io.github.sds100.keymapper.util.ui.compose.SimpleListItemGroup
-import io.github.sds100.keymapper.util.ui.compose.SimpleListItemHeader
-import io.github.sds100.keymapper.util.ui.compose.SimpleListItemModel
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.compose.KeyMapperTheme
+import io.github.sds100.keymapper.base.utils.ui.compose.ComposeIconInfo
+import io.github.sds100.keymapper.base.utils.ui.compose.SearchAppBarActions
+import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemFixedHeight
+import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemGroup
+import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemHeader
+import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemModel
+import io.github.sds100.keymapper.common.utils.State
import kotlinx.coroutines.flow.update
@Composable
fun ChooseActionScreen(
modifier: Modifier = Modifier,
viewModel: ChooseActionViewModel,
- onNavigateBack: () -> Unit,
) {
val state by viewModel.groups.collectAsStateWithLifecycle()
val query by viewModel.searchQuery.collectAsStateWithLifecycle()
@@ -69,11 +67,10 @@ fun ChooseActionScreen(
onQueryChange = { newQuery -> viewModel.searchQuery.update { newQuery } },
onCloseSearch = { viewModel.searchQuery.update { null } },
onClickAction = viewModel::onListItemClick,
- onNavigateBack = onNavigateBack,
+ onNavigateBack = viewModel::onNavigateBack,
)
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ChooseActionScreen(
modifier: Modifier = Modifier,
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/ChooseActionViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ChooseActionViewModel.kt
similarity index 75%
rename from app/src/main/java/io/github/sds100/keymapper/actions/ChooseActionViewModel.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/ChooseActionViewModel.kt
index 10ea36c237..895035c7d8 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/ChooseActionViewModel.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ChooseActionViewModel.kt
@@ -1,25 +1,23 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
-import io.github.sds100.keymapper.R
+import dagger.hilt.android.lifecycle.HiltViewModel
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.utils.containsQuery
+import io.github.sds100.keymapper.base.utils.getFullMessage
+import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider
+import io.github.sds100.keymapper.base.utils.ui.DialogModel
+import io.github.sds100.keymapper.base.utils.ui.DialogProvider
+import io.github.sds100.keymapper.base.utils.ui.DialogResponse
+import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
+import io.github.sds100.keymapper.base.utils.ui.compose.ComposeIconInfo
+import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemGroup
+import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemModel
+import io.github.sds100.keymapper.base.utils.ui.showDialog
+import io.github.sds100.keymapper.common.utils.State
+import io.github.sds100.keymapper.system.SystemError
import io.github.sds100.keymapper.system.permissions.Permission
-import io.github.sds100.keymapper.util.Error
-import io.github.sds100.keymapper.util.State
-import io.github.sds100.keymapper.util.containsQuery
-import io.github.sds100.keymapper.util.getFullMessage
-import io.github.sds100.keymapper.util.ui.DialogResponse
-import io.github.sds100.keymapper.util.ui.NavigationViewModel
-import io.github.sds100.keymapper.util.ui.NavigationViewModelImpl
-import io.github.sds100.keymapper.util.ui.PopupUi
-import io.github.sds100.keymapper.util.ui.PopupViewModel
-import io.github.sds100.keymapper.util.ui.PopupViewModelImpl
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-import io.github.sds100.keymapper.util.ui.compose.ComposeIconInfo
-import io.github.sds100.keymapper.util.ui.compose.SimpleListItemGroup
-import io.github.sds100.keymapper.util.ui.compose.SimpleListItemModel
-import io.github.sds100.keymapper.util.ui.showPopup
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -27,20 +25,21 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
+import kotlinx.serialization.json.Json
+import javax.inject.Inject
-/**
- * Created by sds100 on 22/07/2021.
- */
-class ChooseActionViewModel(
+@HiltViewModel
+class ChooseActionViewModel @Inject constructor(
private val useCase: CreateActionUseCase,
resourceProvider: ResourceProvider,
+ navigationProvider: NavigationProvider,
+ dialogProvider: DialogProvider,
) : ViewModel(),
ResourceProvider by resourceProvider,
- PopupViewModel by PopupViewModelImpl(),
- NavigationViewModel by NavigationViewModelImpl() {
+ DialogProvider by dialogProvider,
+ NavigationProvider by navigationProvider {
companion object {
private val CATEGORY_ORDER = arrayOf(
@@ -66,9 +65,6 @@ class ChooseActionViewModel(
private val allGroupedListItems: List by lazy { buildListGroups() }
- val returnAction = createActionDelegate.actionResult.filterNotNull()
- .shareIn(viewModelScope, SharingStarted.Eagerly)
-
val searchQuery = MutableStateFlow(null)
val groups: StateFlow>> =
@@ -87,19 +83,33 @@ class ChooseActionViewModel(
State.Data(groups)
}.flowOn(Dispatchers.Default).stateIn(viewModelScope, SharingStarted.Eagerly, State.Loading)
+ init {
+ viewModelScope.launch {
+ createActionDelegate.actionResult.filterNotNull().collect { action ->
+ popBackStackWithResult(Json.encodeToString(action))
+ }
+ }
+ }
+
fun onListItemClick(id: String) {
viewModelScope.launch {
val actionId = ActionId.valueOf(id)
val approvedMessage = showMessageForAction(actionId)
if (!approvedMessage) {
- showMessageForAction(actionId)
+ return@launch
}
createActionDelegate.createAction(actionId)
}
}
+ fun onNavigateBack() {
+ viewModelScope.launch {
+ popBackStack()
+ }
+ }
+
private fun buildListGroups(): List = buildList {
val listItems = buildListItems(ActionId.entries)
@@ -142,7 +152,7 @@ class ChooseActionViewModel(
val icon = ActionUtils.getComposeIcon(actionId)
val subtitle = when {
- error == Error.PermissionDenied(Permission.ROOT) -> getString(R.string.choose_action_warning_requires_root)
+ error == SystemError.PermissionDenied(Permission.ROOT) -> getString(R.string.choose_action_warning_requires_root)
error != null -> error.getFullMessage(this@ChooseActionViewModel)
else -> null
}
@@ -166,9 +176,9 @@ class ChooseActionViewModel(
private suspend fun showMessageForAction(id: ActionId): Boolean {
// See issue #1379
if (id == ActionId.APP) {
- val response = showPopup(
+ val response = showDialog(
"show_app_action_warning_dialog",
- PopupUi.Dialog(
+ DialogModel.Alert(
message = getString(R.string.action_open_app_dialog_message),
title = getString(R.string.action_open_app_dialog_title),
positiveButtonText = getString(R.string.action_open_app_dialog_read_more_button),
@@ -177,9 +187,9 @@ class ChooseActionViewModel(
)
if (response == DialogResponse.POSITIVE) {
- showPopup(
+ showDialog(
"app_action_permission_info",
- PopupUi.OpenUrl(getString(R.string.url_action_guide)),
+ DialogModel.OpenUrl(getString(R.string.url_action_guide)),
)
return false
} else {
@@ -210,9 +220,9 @@ class ChooseActionViewModel(
}
if (messageToShow != null) {
- val response = showPopup(
+ val response = showDialog(
"show_action_message",
- PopupUi.Ok(message = getString(messageToShow)),
+ DialogModel.Ok(message = getString(messageToShow)),
)
return response != null
@@ -220,13 +230,4 @@ class ChooseActionViewModel(
return true
}
-
- @Suppress("UNCHECKED_CAST")
- class Factory(
- private val useCase: CreateActionUseCase,
- private val resourceProvider: ResourceProvider,
- ) : ViewModelProvider.NewInstanceFactory() {
-
- override fun create(modelClass: Class): T = ChooseActionViewModel(useCase, resourceProvider) as T
- }
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/ConfigActionsViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ConfigActionsViewModel.kt
similarity index 87%
rename from app/src/main/java/io/github/sds100/keymapper/actions/ConfigActionsViewModel.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/ConfigActionsViewModel.kt
index dfe362ff0b..766d12cfe3 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/ConfigActionsViewModel.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ConfigActionsViewModel.kt
@@ -1,32 +1,31 @@
-package io.github.sds100.keymapper.actions
-
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.keymaps.ConfigKeyMapUseCase
-import io.github.sds100.keymapper.keymaps.KeyMap
-import io.github.sds100.keymapper.keymaps.ShortcutModel
-import io.github.sds100.keymapper.onboarding.OnboardingUseCase
+package io.github.sds100.keymapper.base.actions
+
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.keymaps.ConfigKeyMapUseCase
+import io.github.sds100.keymapper.base.keymaps.KeyMap
+import io.github.sds100.keymapper.base.keymaps.ShortcutModel
+import io.github.sds100.keymapper.base.onboarding.OnboardingUseCase
+import io.github.sds100.keymapper.base.utils.getFullMessage
+import io.github.sds100.keymapper.base.utils.isFixable
+import io.github.sds100.keymapper.base.utils.navigation.NavDestination
+import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider
+import io.github.sds100.keymapper.base.utils.navigation.navigate
+import io.github.sds100.keymapper.base.utils.ui.ChooseAppStoreModel
+import io.github.sds100.keymapper.base.utils.ui.DialogModel
+import io.github.sds100.keymapper.base.utils.ui.DialogProvider
+import io.github.sds100.keymapper.base.utils.ui.DialogResponse
+import io.github.sds100.keymapper.base.utils.ui.LinkType
+import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
+import io.github.sds100.keymapper.base.utils.ui.ViewModelHelper
+import io.github.sds100.keymapper.base.utils.ui.compose.ComposeIconInfo
+import io.github.sds100.keymapper.base.utils.ui.showDialog
+import io.github.sds100.keymapper.common.utils.KMError
+import io.github.sds100.keymapper.common.utils.State
+import io.github.sds100.keymapper.common.utils.dataOrNull
+import io.github.sds100.keymapper.common.utils.mapData
+import io.github.sds100.keymapper.common.utils.onFailure
+import io.github.sds100.keymapper.system.SystemError
import io.github.sds100.keymapper.system.permissions.Permission
-import io.github.sds100.keymapper.util.Error
-import io.github.sds100.keymapper.util.State
-import io.github.sds100.keymapper.util.dataOrNull
-import io.github.sds100.keymapper.util.getFullMessage
-import io.github.sds100.keymapper.util.isFixable
-import io.github.sds100.keymapper.util.mapData
-import io.github.sds100.keymapper.util.onFailure
-import io.github.sds100.keymapper.util.ui.ChooseAppStoreModel
-import io.github.sds100.keymapper.util.ui.DialogResponse
-import io.github.sds100.keymapper.util.ui.LinkType
-import io.github.sds100.keymapper.util.ui.NavDestination
-import io.github.sds100.keymapper.util.ui.NavigationViewModel
-import io.github.sds100.keymapper.util.ui.NavigationViewModelImpl
-import io.github.sds100.keymapper.util.ui.PopupUi
-import io.github.sds100.keymapper.util.ui.PopupViewModel
-import io.github.sds100.keymapper.util.ui.PopupViewModelImpl
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-import io.github.sds100.keymapper.util.ui.ViewModelHelper
-import io.github.sds100.keymapper.util.ui.compose.ComposeIconInfo
-import io.github.sds100.keymapper.util.ui.navigate
-import io.github.sds100.keymapper.util.ui.showPopup
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -41,10 +40,6 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
-/**
- * Created by sds100 on 22/11/20.
- */
-
class ConfigActionsViewModel(
private val coroutineScope: CoroutineScope,
private val displayAction: DisplayActionUseCase,
@@ -53,10 +48,12 @@ class ConfigActionsViewModel(
private val config: ConfigKeyMapUseCase,
private val onboarding: OnboardingUseCase,
resourceProvider: ResourceProvider,
+ navigationProvider: NavigationProvider,
+ dialogProvider: DialogProvider,
) : ActionOptionsBottomSheetCallback,
ResourceProvider by resourceProvider,
- PopupViewModel by PopupViewModelImpl(),
- NavigationViewModel by NavigationViewModelImpl() {
+ DialogProvider by dialogProvider,
+ NavigationProvider by navigationProvider {
val createActionDelegate =
CreateActionDelegate(coroutineScope, createAction, this, this, this)
@@ -115,11 +112,11 @@ class ConfigActionsViewModel(
val error =
actionErrorSnapshot.filterNotNull().first().getError(actionData) ?: return@launch
- if (error == Error.PermissionDenied(Permission.ACCESS_NOTIFICATION_POLICY)) {
+ if (error == SystemError.PermissionDenied(Permission.ACCESS_NOTIFICATION_POLICY)) {
coroutineScope.launch {
ViewModelHelper.showDialogExplainingDndAccessBeingUnavailable(
resourceProvider = this@ConfigActionsViewModel,
- popupViewModel = this@ConfigActionsViewModel,
+ dialogProvider = this@ConfigActionsViewModel,
neverShowDndTriggerErrorAgain = { displayAction.neverShowDndTriggerError() },
fixError = { displayAction.fixError(error) },
)
@@ -127,7 +124,7 @@ class ConfigActionsViewModel(
} else {
ViewModelHelper.showFixErrorDialog(
resourceProvider = this@ConfigActionsViewModel,
- popupViewModel = this@ConfigActionsViewModel,
+ dialogProvider = this@ConfigActionsViewModel,
error,
) {
displayAction.fixError(error)
@@ -254,18 +251,18 @@ class ConfigActionsViewModel(
private suspend fun attemptTestAction(actionData: ActionData) {
testAction.invoke(actionData).onFailure { error ->
- if (error is Error.AccessibilityServiceDisabled) {
+ if (error is KMError.AccessibilityServiceDisabled) {
ViewModelHelper.handleAccessibilityServiceStoppedDialog(
resourceProvider = this,
- popupViewModel = this,
+ dialogProvider = this,
startService = displayAction::startAccessibilityService,
)
}
- if (error is Error.AccessibilityServiceCrashed) {
+ if (error is KMError.AccessibilityServiceCrashed) {
ViewModelHelper.handleAccessibilityServiceCrashedDialog(
resourceProvider = this,
- popupViewModel = this,
+ dialogProvider = this,
restartService = displayAction::restartAccessibilityService,
)
}
@@ -278,7 +275,7 @@ class ConfigActionsViewModel(
githubLink = getString(R.string.url_github_keymapper_leanback_keyboard),
)
- val dialog = PopupUi.ChooseAppStore(
+ val dialog = DialogModel.ChooseAppStore(
title = getString(R.string.dialog_title_install_leanback_keyboard),
message = getString(R.string.dialog_message_install_leanback_keyboard),
appStoreModel,
@@ -286,7 +283,7 @@ class ConfigActionsViewModel(
negativeButtonText = getString(R.string.neg_cancel),
)
- val response = showPopup("download_leanback_ime", dialog) ?: return
+ val response = showDialog("download_leanback_ime", dialog) ?: return
if (response == DialogResponse.POSITIVE) {
onboarding.neverShowGuiKeyboardPromptsAgain()
@@ -298,7 +295,7 @@ class ConfigActionsViewModel(
githubLink = getString(R.string.url_github_keymapper_gui_keyboard),
)
- val dialog = PopupUi.ChooseAppStore(
+ val dialog = DialogModel.ChooseAppStore(
title = getString(R.string.dialog_title_install_gui_keyboard),
message = getString(R.string.dialog_message_install_gui_keyboard),
appStoreModel,
@@ -306,7 +303,7 @@ class ConfigActionsViewModel(
negativeButtonText = getString(R.string.neg_cancel),
)
- val response = showPopup("download_gui_keyboard", dialog) ?: return
+ val response = showDialog("download_gui_keyboard", dialog) ?: return
if (response == DialogResponse.POSITIVE) {
onboarding.neverShowGuiKeyboardPromptsAgain()
@@ -316,7 +313,7 @@ class ConfigActionsViewModel(
private suspend fun promptToInstallShizukuOrGuiKeyboard() {
if (onboarding.isTvDevice()) {
- val chooseSolutionDialog = PopupUi.Dialog(
+ val chooseSolutionDialog = DialogModel.Alert(
title = getText(R.string.dialog_title_install_shizuku_or_leanback_keyboard),
message = getText(R.string.dialog_message_install_shizuku_or_leanback_keyboard),
positiveButtonText = getString(R.string.dialog_button_install_shizuku),
@@ -325,7 +322,7 @@ class ConfigActionsViewModel(
)
val chooseSolutionResponse =
- showPopup("choose_solution", chooseSolutionDialog) ?: return
+ showDialog("choose_solution", chooseSolutionDialog) ?: return
when (chooseSolutionResponse) {
// install shizuku
@@ -343,7 +340,7 @@ class ConfigActionsViewModel(
// download leanback keyboard
DialogResponse.NEGATIVE -> {
- val chooseAppStoreDialog = PopupUi.ChooseAppStore(
+ val chooseAppStoreDialog = DialogModel.ChooseAppStore(
title = getString(R.string.dialog_title_choose_download_leanback_keyboard),
message = getString(R.string.dialog_message_choose_download_leanback_keyboard),
model = ChooseAppStoreModel(
@@ -353,7 +350,7 @@ class ConfigActionsViewModel(
negativeButtonText = getString(R.string.neg_cancel),
)
- val response = showPopup("install_leanback_keyboard", chooseAppStoreDialog)
+ val response = showDialog("install_leanback_keyboard", chooseAppStoreDialog)
if (response == DialogResponse.POSITIVE) {
onboarding.neverShowGuiKeyboardPromptsAgain()
@@ -361,7 +358,7 @@ class ConfigActionsViewModel(
}
}
} else {
- val chooseSolutionDialog = PopupUi.Dialog(
+ val chooseSolutionDialog = DialogModel.Alert(
title = getText(R.string.dialog_title_install_shizuku_or_gui_keyboard),
message = getText(R.string.dialog_message_install_shizuku_or_gui_keyboard),
positiveButtonText = getString(R.string.dialog_button_install_shizuku),
@@ -370,7 +367,7 @@ class ConfigActionsViewModel(
)
val chooseSolutionResponse =
- showPopup("choose_solution", chooseSolutionDialog) ?: return
+ showDialog("choose_solution", chooseSolutionDialog) ?: return
when (chooseSolutionResponse) {
// install shizuku
@@ -388,7 +385,7 @@ class ConfigActionsViewModel(
// download gui keyboard
DialogResponse.NEGATIVE -> {
- val chooseAppStoreDialog = PopupUi.ChooseAppStore(
+ val chooseAppStoreDialog = DialogModel.ChooseAppStore(
title = getString(R.string.dialog_title_choose_download_gui_keyboard),
message = getString(R.string.dialog_message_choose_download_gui_keyboard),
model = ChooseAppStoreModel(
@@ -400,7 +397,7 @@ class ConfigActionsViewModel(
negativeButtonText = getString(R.string.neg_cancel),
)
- val response = showPopup("install_gui_keyboard", chooseAppStoreDialog)
+ val response = showDialog("install_gui_keyboard", chooseAppStoreDialog)
if (response == DialogResponse.POSITIVE) {
onboarding.neverShowGuiKeyboardPromptsAgain()
@@ -454,7 +451,7 @@ class ConfigActionsViewModel(
}
val icon: ComposeIconInfo = uiHelper.getIcon(action.data)
- val error: Error? = errorSnapshot.getError(action.data)
+ val error: KMError? = errorSnapshot.getError(action.data)
val extraInfo = buildString {
val midDot = getString(R.string.middot)
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/CreateActionDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/CreateActionDelegate.kt
similarity index 88%
rename from app/src/main/java/io/github/sds100/keymapper/actions/CreateActionDelegate.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/CreateActionDelegate.kt
index e034b3e9f8..5d5cd0d305 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/CreateActionDelegate.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/CreateActionDelegate.kt
@@ -1,53 +1,48 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
import android.text.InputType
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.actions.pinchscreen.PinchPickCoordinateResult
-import io.github.sds100.keymapper.actions.swipescreen.SwipePickCoordinateResult
-import io.github.sds100.keymapper.actions.tapscreen.PickCoordinateResult
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.actions.pinchscreen.PinchPickCoordinateResult
+import io.github.sds100.keymapper.base.actions.swipescreen.SwipePickCoordinateResult
+import io.github.sds100.keymapper.base.actions.tapscreen.PickCoordinateResult
+import io.github.sds100.keymapper.base.system.intents.ConfigIntentResult
+import io.github.sds100.keymapper.base.utils.DndModeStrings
+import io.github.sds100.keymapper.base.utils.RingerModeStrings
+import io.github.sds100.keymapper.base.utils.VolumeStreamStrings
+import io.github.sds100.keymapper.base.utils.navigation.NavDestination
+import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider
+import io.github.sds100.keymapper.base.utils.navigation.navigate
+import io.github.sds100.keymapper.base.utils.ui.DialogModel
+import io.github.sds100.keymapper.base.utils.ui.DialogProvider
+import io.github.sds100.keymapper.base.utils.ui.MultiChoiceItem
+import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
+import io.github.sds100.keymapper.base.utils.ui.showDialog
+import io.github.sds100.keymapper.common.utils.Orientation
import io.github.sds100.keymapper.system.camera.CameraLens
-import io.github.sds100.keymapper.system.camera.CameraLensUtils
-import io.github.sds100.keymapper.system.display.Orientation
-import io.github.sds100.keymapper.system.display.OrientationUtils
-import io.github.sds100.keymapper.system.intents.ConfigIntentResult
import io.github.sds100.keymapper.system.network.HttpMethod
import io.github.sds100.keymapper.system.volume.DndMode
-import io.github.sds100.keymapper.system.volume.DndModeUtils
import io.github.sds100.keymapper.system.volume.RingerMode
-import io.github.sds100.keymapper.system.volume.RingerModeUtils
import io.github.sds100.keymapper.system.volume.VolumeStream
-import io.github.sds100.keymapper.system.volume.VolumeStreamUtils
-import io.github.sds100.keymapper.util.ui.MultiChoiceItem
-import io.github.sds100.keymapper.util.ui.NavDestination
-import io.github.sds100.keymapper.util.ui.NavigationViewModel
-import io.github.sds100.keymapper.util.ui.PopupUi
-import io.github.sds100.keymapper.util.ui.PopupViewModel
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-import io.github.sds100.keymapper.util.ui.navigate
-import io.github.sds100.keymapper.util.ui.showPopup
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
-
-/**
- * Created by sds100 on 26/07/2021.
- */
+import kotlinx.serialization.json.Json
class CreateActionDelegate(
private val coroutineScope: CoroutineScope,
private val useCase: CreateActionUseCase,
- popupViewModel: PopupViewModel,
- navigationViewModelImpl: NavigationViewModel,
+ dialogProvider: DialogProvider,
+ navigationProvider: NavigationProvider,
resourceProvider: ResourceProvider,
) : ResourceProvider by resourceProvider,
- PopupViewModel by popupViewModel,
- NavigationViewModel by navigationViewModelImpl {
+ DialogProvider by dialogProvider,
+ NavigationProvider by navigationProvider {
val actionResult: MutableStateFlow = MutableStateFlow(null)
var enableFlashlightActionState: EnableFlashlightActionState? by mutableStateOf(null)
@@ -168,7 +163,7 @@ class CreateActionDelegate(
}
val imeId =
- showPopup("choose_ime", PopupUi.SingleChoice(items)) ?: return null
+ showDialog("choose_ime", DialogModel.SingleChoice(items)) ?: return null
val imeName = inputMethods.single { it.id == imeId }.label
return ActionData.SwitchKeyboard(imeId, imeName)
@@ -254,9 +249,9 @@ class CreateActionDelegate(
),
)
- val showVolumeUiDialog = PopupUi.MultiChoice(items = dialogItems)
+ val showVolumeUiDialog = DialogModel.MultiChoice(items = dialogItems)
- val chosenFlags = showPopup("show_volume_ui", showVolumeUiDialog) ?: return null
+ val chosenFlags = showDialog("show_volume_ui", showVolumeUiDialog) ?: return null
val showVolumeUi = chosenFlags.contains(showVolumeUiId)
@@ -293,17 +288,17 @@ class CreateActionDelegate(
),
)
- val showVolumeUiDialog = PopupUi.MultiChoice(items = dialogItems)
+ val showVolumeUiDialog = DialogModel.MultiChoice(items = dialogItems)
val chosenFlags =
- showPopup("show_volume_ui", showVolumeUiDialog) ?: return null
+ showDialog("show_volume_ui", showVolumeUiDialog) ?: return null
val showVolumeUi = chosenFlags.contains(showVolumeUiId)
- val items = VolumeStream.values()
- .map { it to getString(VolumeStreamUtils.getLabel(it)) }
+ val items = VolumeStream.entries
+ .map { it to getString(VolumeStreamStrings.getLabel(it)) }
- val stream = showPopup("pick_volume_stream", PopupUi.SingleChoice(items))
+ val stream = showDialog("pick_volume_stream", DialogModel.SingleChoice(items))
?: return null
val action = when (actionId) {
@@ -320,11 +315,11 @@ class CreateActionDelegate(
}
ActionId.CHANGE_RINGER_MODE -> {
- val items = RingerMode.values()
- .map { it to getString(RingerModeUtils.getLabel(it)) }
+ val items = RingerMode.entries
+ .map { it to getString(RingerModeStrings.getLabel(it)) }
val ringerMode =
- showPopup("pick_ringer_mode", PopupUi.SingleChoice(items))
+ showDialog("pick_ringer_mode", DialogModel.SingleChoice(items))
?: return null
return ActionData.Volume.SetRingerMode(ringerMode)
@@ -334,11 +329,11 @@ class CreateActionDelegate(
ActionId.TOGGLE_DND_MODE,
ActionId.ENABLE_DND_MODE,
-> {
- val items = DndMode.values()
- .map { it to getString(DndModeUtils.getLabel(it)) }
+ val items = DndMode.entries
+ .map { it to getString(DndModeStrings.getLabel(it)) }
val dndMode =
- showPopup("pick_dnd_mode", PopupUi.SingleChoice(items))
+ showDialog("pick_dnd_mode", DialogModel.SingleChoice(items))
?: return null
val action = when (actionId) {
@@ -360,15 +355,22 @@ class CreateActionDelegate(
false
}
+ val label = when (orientation) {
+ Orientation.ORIENTATION_0 -> R.string.orientation_0
+ Orientation.ORIENTATION_90 -> R.string.orientation_90
+ Orientation.ORIENTATION_180 -> R.string.orientation_180
+ Orientation.ORIENTATION_270 -> R.string.orientation_270
+ }
+
MultiChoiceItem(
orientation,
- getString(OrientationUtils.getLabel(orientation)),
+ getString(label),
isChecked,
)
}
val orientations =
- showPopup("pick_orientations", PopupUi.MultiChoice(items)) ?: return null
+ showDialog("pick_orientations", DialogModel.MultiChoice(items)) ?: return null
return ActionData.Rotation.CycleRotations(orientations)
}
@@ -445,14 +447,17 @@ class CreateActionDelegate(
ActionId.DISABLE_FLASHLIGHT,
-> {
- val items = useCase.getFlashlightLenses().map {
- it to getString(CameraLensUtils.getLabel(it))
+ val items = useCase.getFlashlightLenses().map { lens ->
+ when (lens) {
+ CameraLens.FRONT -> lens to getString(R.string.lens_front)
+ CameraLens.BACK -> lens to getString(R.string.lens_back)
+ }
}
if (items.size == 1) {
return ActionData.Flashlight.Disable(items.first().first)
} else {
- val lens = showPopup("pick_lens", PopupUi.SingleChoice(items))
+ val lens = showDialog("pick_lens", DialogModel.SingleChoice(items))
?: return null
return ActionData.Flashlight.Disable(lens)
@@ -484,8 +489,7 @@ class CreateActionDelegate(
ActionId.KEY_CODE -> {
val keyCode =
- navigate("choose_key_code", NavDestination.ChooseKeyCode)
- ?: return null
+ navigate("choose_key_code", NavDestination.ChooseKeyCode) ?: return null
return ActionData.InputKeyEvent(keyCode = keyCode)
}
@@ -610,9 +614,9 @@ class CreateActionDelegate(
""
}
- val text = showPopup(
+ val text = showDialog(
"create_text_action",
- PopupUi.Text(
+ DialogModel.Text(
hint = getString(R.string.hint_create_text_action),
allowEmpty = false,
text = oldText,
@@ -629,9 +633,9 @@ class CreateActionDelegate(
""
}
- val text = showPopup(
+ val text = showDialog(
"create_url_action",
- PopupUi.Text(
+ DialogModel.Text(
hint = getString(R.string.hint_create_url_action),
allowEmpty = false,
inputType = InputType.TYPE_TEXT_VARIATION_URI,
@@ -674,9 +678,9 @@ class CreateActionDelegate(
""
}
- val text = showPopup(
+ val text = showDialog(
"create_phone_call_action",
- PopupUi.Text(
+ DialogModel.Text(
hint = getString(R.string.hint_create_phone_call_action),
allowEmpty = false,
inputType = InputType.TYPE_CLASS_PHONE,
@@ -802,7 +806,7 @@ class CreateActionDelegate(
return navigate(
"config_interact_ui_element_action",
- NavDestination.InteractUiElement(oldAction),
+ NavDestination.InteractUiElement(oldAction?.let { Json.encodeToString(it) }),
)
}
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/CreateActionUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/CreateActionUseCase.kt
similarity index 95%
rename from app/src/main/java/io/github/sds100/keymapper/actions/CreateActionUseCase.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/CreateActionUseCase.kt
index 37ddcecac2..90ba5f6d72 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/CreateActionUseCase.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/CreateActionUseCase.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
import io.github.sds100.keymapper.system.camera.CameraAdapter
import io.github.sds100.keymapper.system.camera.CameraFlashInfo
@@ -10,12 +10,9 @@ import io.github.sds100.keymapper.system.permissions.SystemFeatureAdapter
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.merge
+import javax.inject.Inject
-/**
- * Created by sds100 on 25/07/2021.
- */
-
-class CreateActionUseCaseImpl(
+class CreateActionUseCaseImpl @Inject constructor(
private val inputMethodAdapter: InputMethodAdapter,
private val systemFeatureAdapter: SystemFeatureAdapter,
private val cameraAdapter: CameraAdapter,
diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/DisplayActionUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/DisplayActionUseCase.kt
new file mode 100644
index 0000000000..ceb8958484
--- /dev/null
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/DisplayActionUseCase.kt
@@ -0,0 +1,18 @@
+package io.github.sds100.keymapper.base.actions
+
+import android.graphics.drawable.Drawable
+import io.github.sds100.keymapper.common.utils.KMError
+import io.github.sds100.keymapper.common.utils.KMResult
+import kotlinx.coroutines.flow.Flow
+
+interface DisplayActionUseCase : GetActionErrorUseCase {
+ val showDeviceDescriptors: Flow
+ fun getAppName(packageName: String): KMResult
+ fun getAppIcon(packageName: String): KMResult
+ fun getInputMethodLabel(imeId: String): KMResult
+ fun getRingtoneLabel(uri: String): KMResult
+ suspend fun fixError(error: KMError)
+ fun neverShowDndTriggerError()
+ fun startAccessibilityService(): Boolean
+ fun restartAccessibilityService(): Boolean
+}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/FlashlightActionBottomSheet.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/FlashlightActionBottomSheet.kt
similarity index 98%
rename from app/src/main/java/io/github/sds100/keymapper/actions/FlashlightActionBottomSheet.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/FlashlightActionBottomSheet.kt
index 1482dd5327..13b4490d76 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/FlashlightActionBottomSheet.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/FlashlightActionBottomSheet.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
import android.os.Build
import androidx.compose.animation.AnimatedContent
@@ -46,13 +46,13 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.compose.KeyMapperTheme
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.compose.KeyMapperTheme
+import io.github.sds100.keymapper.base.utils.ui.compose.KeyMapperSliderThumb
+import io.github.sds100.keymapper.base.utils.ui.compose.OptionsHeaderRow
+import io.github.sds100.keymapper.base.utils.ui.compose.RadioButtonText
import io.github.sds100.keymapper.system.camera.CameraFlashInfo
import io.github.sds100.keymapper.system.camera.CameraLens
-import io.github.sds100.keymapper.util.ui.compose.KeyMapperSliderThumb
-import io.github.sds100.keymapper.util.ui.compose.OptionsHeaderRow
-import io.github.sds100.keymapper.util.ui.compose.RadioButtonText
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/GetActionErrorUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/GetActionErrorUseCase.kt
similarity index 77%
rename from app/src/main/java/io/github/sds100/keymapper/actions/GetActionErrorUseCase.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/GetActionErrorUseCase.kt
index ce8a18b9e1..d165b5025b 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/GetActionErrorUseCase.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/GetActionErrorUseCase.kt
@@ -1,22 +1,26 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
-import io.github.sds100.keymapper.actions.sound.SoundsManager
-import io.github.sds100.keymapper.shizuku.ShizukuAdapter
+import io.github.sds100.keymapper.base.actions.sound.SoundsManager
+import io.github.sds100.keymapper.common.BuildConfigProvider
import io.github.sds100.keymapper.system.apps.PackageManagerAdapter
import io.github.sds100.keymapper.system.camera.CameraAdapter
import io.github.sds100.keymapper.system.inputmethod.InputMethodAdapter
import io.github.sds100.keymapper.system.permissions.PermissionAdapter
import io.github.sds100.keymapper.system.permissions.SystemFeatureAdapter
import io.github.sds100.keymapper.system.ringtones.RingtoneAdapter
+import io.github.sds100.keymapper.system.shizuku.ShizukuAdapter
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import javax.inject.Inject
+import javax.inject.Singleton
-class GetActionErrorUseCaseImpl(
- private val packageManager: PackageManagerAdapter,
+@Singleton
+class GetActionErrorUseCaseImpl @Inject constructor(
+ private val packageManagerAdapter: PackageManagerAdapter,
private val inputMethodAdapter: InputMethodAdapter,
private val permissionAdapter: PermissionAdapter,
private val systemFeatureAdapter: SystemFeatureAdapter,
@@ -24,6 +28,7 @@ class GetActionErrorUseCaseImpl(
private val soundsManager: SoundsManager,
private val shizukuAdapter: ShizukuAdapter,
private val ringtoneAdapter: RingtoneAdapter,
+ private val buildConfigProvider: BuildConfigProvider,
) : GetActionErrorUseCase {
private val invalidateActionErrors = merge(
@@ -34,7 +39,7 @@ class GetActionErrorUseCaseImpl(
soundsManager.soundFiles.drop(1).map { },
shizukuAdapter.isStarted.drop(1).map { },
shizukuAdapter.isInstalled.drop(1).map { },
- packageManager.onPackagesChanged,
+ packageManagerAdapter.onPackagesChanged,
)
override val actionErrorSnapshot: Flow = channelFlow {
@@ -46,7 +51,7 @@ class GetActionErrorUseCaseImpl(
}
private fun createSnapshot(): ActionErrorSnapshot = LazyActionErrorSnapshot(
- packageManager,
+ packageManagerAdapter,
inputMethodAdapter,
permissionAdapter,
systemFeatureAdapter,
@@ -54,6 +59,7 @@ class GetActionErrorUseCaseImpl(
soundsManager,
shizukuAdapter,
ringtoneAdapter,
+ buildConfigProvider,
)
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/HoldDownMode.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/HoldDownMode.kt
similarity index 61%
rename from app/src/main/java/io/github/sds100/keymapper/actions/HoldDownMode.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/HoldDownMode.kt
index 886b5a2d68..cf10c89e7e 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/HoldDownMode.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/HoldDownMode.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
enum class HoldDownMode {
TRIGGER_RELEASED,
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/HttpRequestBottomSheet.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/HttpRequestBottomSheet.kt
similarity index 98%
rename from app/src/main/java/io/github/sds100/keymapper/actions/HttpRequestBottomSheet.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/HttpRequestBottomSheet.kt
index 15d0063245..696b6f2f9f 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/HttpRequestBottomSheet.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/HttpRequestBottomSheet.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -37,10 +37,10 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.compose.KeyMapperTheme
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.compose.KeyMapperTheme
+import io.github.sds100.keymapper.base.utils.ui.compose.KeyMapperDropdownMenu
import io.github.sds100.keymapper.system.network.HttpMethod
-import io.github.sds100.keymapper.util.ui.compose.KeyMapperDropdownMenu
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/IsActionSupportedUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/IsActionSupportedUseCase.kt
similarity index 73%
rename from app/src/main/java/io/github/sds100/keymapper/actions/IsActionSupportedUseCase.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/IsActionSupportedUseCase.kt
index b488ea686a..db65db77d6 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/IsActionSupportedUseCase.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/IsActionSupportedUseCase.kt
@@ -1,13 +1,14 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
import android.content.pm.PackageManager
import android.os.Build
+import io.github.sds100.keymapper.common.utils.KMError
+import io.github.sds100.keymapper.system.SystemError
import io.github.sds100.keymapper.system.camera.CameraAdapter
import io.github.sds100.keymapper.system.camera.CameraLens
import io.github.sds100.keymapper.system.permissions.Permission
import io.github.sds100.keymapper.system.permissions.PermissionAdapter
import io.github.sds100.keymapper.system.permissions.SystemFeatureAdapter
-import io.github.sds100.keymapper.util.Error
class IsActionSupportedUseCaseImpl(
private val adapter: SystemFeatureAdapter,
@@ -15,24 +16,24 @@ class IsActionSupportedUseCaseImpl(
private val permissionAdapter: PermissionAdapter,
) : IsActionSupportedUseCase {
- override fun isSupported(id: ActionId): Error? {
+ override fun isSupported(id: ActionId): KMError? {
if (Build.VERSION.SDK_INT != 0) {
val minApi = ActionUtils.getMinApi(id)
if (Build.VERSION.SDK_INT < minApi) {
- return Error.SdkVersionTooLow(minApi)
+ return KMError.SdkVersionTooLow(minApi)
}
val maxApi = ActionUtils.getMaxApi(id)
if (Build.VERSION.SDK_INT > maxApi) {
- return Error.SdkVersionTooHigh(maxApi)
+ return KMError.SdkVersionTooHigh(maxApi)
}
}
ActionUtils.getRequiredSystemFeatures(id).forEach { feature ->
if (!adapter.hasSystemFeature(feature)) {
- return Error.SystemFeatureNotSupported(feature)
+ return KMError.SystemFeatureNotSupported(feature)
}
}
@@ -40,7 +41,7 @@ class IsActionSupportedUseCaseImpl(
if (cameraAdapter.getFlashInfo(CameraLens.BACK) == null &&
cameraAdapter.getFlashInfo(CameraLens.FRONT) == null
) {
- return Error.SystemFeatureNotSupported(PackageManager.FEATURE_CAMERA_FLASH)
+ return KMError.SystemFeatureNotSupported(PackageManager.FEATURE_CAMERA_FLASH)
}
}
@@ -48,7 +49,7 @@ class IsActionSupportedUseCaseImpl(
if (cameraAdapter.getFlashInfo(CameraLens.BACK)?.supportsVariableStrength != true &&
cameraAdapter.getFlashInfo(CameraLens.FRONT)?.supportsVariableStrength != true
) {
- return Error.CameraVariableFlashlightStrengthUnsupported
+ return KMError.CameraVariableFlashlightStrengthUnsupported
}
}
@@ -56,7 +57,7 @@ class IsActionSupportedUseCaseImpl(
.contains(Permission.ROOT) &&
!permissionAdapter.isGranted(Permission.ROOT)
) {
- return Error.PermissionDenied(Permission.ROOT)
+ return SystemError.PermissionDenied(Permission.ROOT)
}
return null
@@ -64,5 +65,5 @@ class IsActionSupportedUseCaseImpl(
}
interface IsActionSupportedUseCase {
- fun isSupported(id: ActionId): Error?
+ fun isSupported(id: ActionId): KMError?
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/PerformActionsUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/PerformActionsUseCase.kt
similarity index 77%
rename from app/src/main/java/io/github/sds100/keymapper/actions/PerformActionsUseCase.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/PerformActionsUseCase.kt
index 11d66a7ae0..f90722be3c 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/PerformActionsUseCase.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/PerformActionsUseCase.kt
@@ -1,19 +1,40 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
import android.accessibilityservice.AccessibilityService
import android.os.Build
import android.view.InputDevice
import android.view.KeyEvent
import android.view.accessibility.AccessibilityNodeInfo
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.actions.sound.SoundsManager
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.actions.sound.SoundsManager
+import io.github.sds100.keymapper.base.system.accessibility.AccessibilityNodeAction
+import io.github.sds100.keymapper.base.system.accessibility.AccessibilityNodeModel
+import io.github.sds100.keymapper.base.system.accessibility.IAccessibilityService
+import io.github.sds100.keymapper.base.system.inputmethod.ImeInputEventInjector
+import io.github.sds100.keymapper.base.system.navigation.OpenMenuHelper
+import io.github.sds100.keymapper.base.utils.getFullMessage
+import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
+import io.github.sds100.keymapper.common.utils.InputEventType
+import io.github.sds100.keymapper.common.utils.KMError
+import io.github.sds100.keymapper.common.utils.KMResult
+import io.github.sds100.keymapper.common.utils.Orientation
+import io.github.sds100.keymapper.common.utils.Success
+import io.github.sds100.keymapper.common.utils.dataOrNull
+import io.github.sds100.keymapper.common.utils.firstBlocking
+import io.github.sds100.keymapper.common.utils.getWordBoundaries
+import io.github.sds100.keymapper.common.utils.ifIsData
+import io.github.sds100.keymapper.common.utils.onFailure
+import io.github.sds100.keymapper.common.utils.onSuccess
+import io.github.sds100.keymapper.common.utils.otherwise
+import io.github.sds100.keymapper.common.utils.success
+import io.github.sds100.keymapper.common.utils.then
+import io.github.sds100.keymapper.common.utils.withFlag
import io.github.sds100.keymapper.data.Keys
import io.github.sds100.keymapper.data.PreferenceDefaults
import io.github.sds100.keymapper.data.repositories.PreferenceRepository
-import io.github.sds100.keymapper.system.accessibility.AccessibilityNodeAction
-import io.github.sds100.keymapper.system.accessibility.AccessibilityNodeModel
-import io.github.sds100.keymapper.system.accessibility.IAccessibilityService
-import io.github.sds100.keymapper.system.accessibility.ServiceAdapter
import io.github.sds100.keymapper.system.airplanemode.AirplaneModeAdapter
import io.github.sds100.keymapper.system.apps.AppShortcutAdapter
import io.github.sds100.keymapper.system.apps.PackageManagerAdapter
@@ -21,48 +42,31 @@ import io.github.sds100.keymapper.system.bluetooth.BluetoothAdapter
import io.github.sds100.keymapper.system.camera.CameraAdapter
import io.github.sds100.keymapper.system.devices.DevicesAdapter
import io.github.sds100.keymapper.system.display.DisplayAdapter
-import io.github.sds100.keymapper.system.display.Orientation
import io.github.sds100.keymapper.system.files.FileAdapter
import io.github.sds100.keymapper.system.files.FileUtils
-import io.github.sds100.keymapper.system.inputevents.InputEventInjector
import io.github.sds100.keymapper.system.inputevents.InputEventUtils
-import io.github.sds100.keymapper.system.inputmethod.ImeInputEventInjector
import io.github.sds100.keymapper.system.inputmethod.InputKeyModel
import io.github.sds100.keymapper.system.inputmethod.InputMethodAdapter
import io.github.sds100.keymapper.system.intents.IntentAdapter
import io.github.sds100.keymapper.system.intents.IntentTarget
import io.github.sds100.keymapper.system.lock.LockScreenAdapter
import io.github.sds100.keymapper.system.media.MediaAdapter
-import io.github.sds100.keymapper.system.navigation.OpenMenuHelper
import io.github.sds100.keymapper.system.network.NetworkAdapter
import io.github.sds100.keymapper.system.nfc.NfcAdapter
+import io.github.sds100.keymapper.system.notifications.NotificationReceiverAdapter
+import io.github.sds100.keymapper.system.notifications.NotificationServiceEvent
import io.github.sds100.keymapper.system.permissions.Permission
import io.github.sds100.keymapper.system.permissions.PermissionAdapter
import io.github.sds100.keymapper.system.phone.PhoneAdapter
-import io.github.sds100.keymapper.system.popup.PopupMessageAdapter
+import io.github.sds100.keymapper.system.popup.ToastAdapter
import io.github.sds100.keymapper.system.ringtones.RingtoneAdapter
import io.github.sds100.keymapper.system.root.SuAdapter
import io.github.sds100.keymapper.system.shell.ShellAdapter
+import io.github.sds100.keymapper.system.shizuku.ShizukuInputEventInjector
import io.github.sds100.keymapper.system.url.OpenUrlAdapter
import io.github.sds100.keymapper.system.volume.RingerMode
import io.github.sds100.keymapper.system.volume.VolumeAdapter
import io.github.sds100.keymapper.system.volume.VolumeStream
-import io.github.sds100.keymapper.util.Error
-import io.github.sds100.keymapper.util.InputEventType
-import io.github.sds100.keymapper.util.Result
-import io.github.sds100.keymapper.util.ServiceEvent
-import io.github.sds100.keymapper.util.Success
-import io.github.sds100.keymapper.util.dataOrNull
-import io.github.sds100.keymapper.util.firstBlocking
-import io.github.sds100.keymapper.util.getFullMessage
-import io.github.sds100.keymapper.util.getWordBoundaries
-import io.github.sds100.keymapper.util.ifIsData
-import io.github.sds100.keymapper.util.onFailure
-import io.github.sds100.keymapper.util.onSuccess
-import io.github.sds100.keymapper.util.otherwise
-import io.github.sds100.keymapper.util.success
-import io.github.sds100.keymapper.util.then
-import io.github.sds100.keymapper.util.ui.ResourceProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
@@ -71,30 +75,26 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import splitties.bitflags.withFlag
import timber.log.Timber
-/**
- * Created by sds100 on 14/02/21.
- */
-
-class PerformActionsUseCaseImpl(
- private val coroutineScope: CoroutineScope,
- private val accessibilityService: IAccessibilityService,
+class PerformActionsUseCaseImpl @AssistedInject constructor(
+ private val appCoroutineScope: CoroutineScope,
+ @Assisted
+ private val service: IAccessibilityService,
private val inputMethodAdapter: InputMethodAdapter,
private val fileAdapter: FileAdapter,
private val suAdapter: SuAdapter,
- private val shellAdapter: ShellAdapter,
+ private val shell: ShellAdapter,
private val intentAdapter: IntentAdapter,
- private val getActionError: GetActionErrorUseCase,
- private val imeInputEventInjector: ImeInputEventInjector,
- private val shizukuInputEventInjector: InputEventInjector,
+ private val getActionErrorUseCase: GetActionErrorUseCase,
+ @Assisted
+ private val keyMapperImeMessenger: ImeInputEventInjector,
private val packageManagerAdapter: PackageManagerAdapter,
private val appShortcutAdapter: AppShortcutAdapter,
- private val popupMessageAdapter: PopupMessageAdapter,
- private val deviceAdapter: DevicesAdapter,
+ private val toastAdapter: ToastAdapter,
+ private val devicesAdapter: DevicesAdapter,
private val phoneAdapter: PhoneAdapter,
- private val volumeAdapter: VolumeAdapter,
+ private val audioAdapter: VolumeAdapter,
private val cameraAdapter: CameraAdapter,
private val displayAdapter: DisplayAdapter,
private val lockScreenAdapter: LockScreenAdapter,
@@ -105,20 +105,30 @@ class PerformActionsUseCaseImpl(
private val nfcAdapter: NfcAdapter,
private val openUrlAdapter: OpenUrlAdapter,
private val resourceProvider: ResourceProvider,
- private val preferenceRepository: PreferenceRepository,
private val soundsManager: SoundsManager,
private val permissionAdapter: PermissionAdapter,
- private val notificationReceiverAdapter: ServiceAdapter,
+ private val notificationReceiverAdapter: NotificationReceiverAdapter,
private val ringtoneAdapter: RingtoneAdapter,
+ private val settingsRepository: PreferenceRepository,
) : PerformActionsUseCase {
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ accessibilityService: IAccessibilityService,
+ imeInputEventInjector: ImeInputEventInjector,
+ ): PerformActionsUseCaseImpl
+ }
+
+ private val shizukuInputEventInjector: ShizukuInputEventInjector = ShizukuInputEventInjector()
+
private val openMenuHelper by lazy {
OpenMenuHelper(
suAdapter,
- accessibilityService,
+ service,
shizukuInputEventInjector,
permissionAdapter,
- coroutineScope,
+ appCoroutineScope,
)
}
@@ -127,7 +137,7 @@ class PerformActionsUseCaseImpl(
*/
private val inputKeyEventsWithShizuku: StateFlow =
permissionAdapter.isGrantedFlow(Permission.SHIZUKU)
- .stateIn(coroutineScope, SharingStarted.Eagerly, false)
+ .stateIn(appCoroutineScope, SharingStarted.Eagerly, false)
override suspend fun perform(
action: ActionData,
@@ -137,7 +147,7 @@ class PerformActionsUseCaseImpl(
/**
* Is null if the action is being performed asynchronously
*/
- val result: Result<*>
+ val result: KMResult<*>
when (action) {
is ActionData.App -> {
@@ -179,7 +189,7 @@ class PerformActionsUseCaseImpl(
action.useShell -> suAdapter.execute("input keyevent ${model.keyCode}")
else -> {
- imeInputEventInjector.inputKeyEvent(model)
+ keyMapperImeMessenger.inputKeyEvent(model)
Success(Unit)
}
@@ -191,19 +201,19 @@ class PerformActionsUseCaseImpl(
}
is ActionData.DoNotDisturb.Enable -> {
- result = volumeAdapter.enableDndMode(action.dndMode)
+ result = audioAdapter.enableDndMode(action.dndMode)
}
is ActionData.DoNotDisturb.Toggle -> {
- result = if (volumeAdapter.isDndEnabled()) {
- volumeAdapter.disableDndMode()
+ result = if (audioAdapter.isDndEnabled()) {
+ audioAdapter.disableDndMode()
} else {
- volumeAdapter.enableDndMode(action.dndMode)
+ audioAdapter.enableDndMode(action.dndMode)
}
}
is ActionData.Volume.SetRingerMode -> {
- result = volumeAdapter.setRingerMode(action.ringerMode)
+ result = audioAdapter.setRingerMode(action.ringerMode)
}
is ActionData.ControlMediaForApp.FastForward -> {
@@ -286,50 +296,50 @@ class PerformActionsUseCaseImpl(
R.string.toast_chose_keyboard,
it.label,
)
- popupMessageAdapter.showPopupMessage(message)
+ toastAdapter.show(message)
}
}
is ActionData.Volume.Down -> {
- result = volumeAdapter.lowerVolume(showVolumeUi = action.showVolumeUi)
+ result = audioAdapter.lowerVolume(showVolumeUi = action.showVolumeUi)
}
is ActionData.Volume.Up -> {
- result = volumeAdapter.raiseVolume(showVolumeUi = action.showVolumeUi)
+ result = audioAdapter.raiseVolume(showVolumeUi = action.showVolumeUi)
}
is ActionData.Volume.Mute -> {
- result = volumeAdapter.muteVolume(showVolumeUi = action.showVolumeUi)
+ result = audioAdapter.muteVolume(showVolumeUi = action.showVolumeUi)
}
is ActionData.Volume.Stream.Decrease -> {
- result = volumeAdapter.lowerVolume(
+ result = audioAdapter.lowerVolume(
stream = action.volumeStream,
showVolumeUi = action.showVolumeUi,
)
}
is ActionData.Volume.Stream.Increase -> {
- result = volumeAdapter.raiseVolume(
+ result = audioAdapter.raiseVolume(
stream = action.volumeStream,
showVolumeUi = action.showVolumeUi,
)
}
is ActionData.Volume.ToggleMute -> {
- result = volumeAdapter.toggleMuteVolume(showVolumeUi = action.showVolumeUi)
+ result = audioAdapter.toggleMuteVolume(showVolumeUi = action.showVolumeUi)
}
is ActionData.Volume.UnMute -> {
- result = volumeAdapter.unmuteVolume(showVolumeUi = action.showVolumeUi)
+ result = audioAdapter.unmuteVolume(showVolumeUi = action.showVolumeUi)
}
is ActionData.TapScreen -> {
- result = accessibilityService.tapScreen(action.x, action.y, inputEventType)
+ result = service.tapScreen(action.x, action.y, inputEventType)
}
is ActionData.SwipeScreen -> {
- result = accessibilityService.swipeScreen(
+ result = service.swipeScreen(
action.xStart,
action.yStart,
action.xEnd,
@@ -341,7 +351,7 @@ class PerformActionsUseCaseImpl(
}
is ActionData.PinchScreen -> {
- result = accessibilityService.pinchScreen(
+ result = service.pinchScreen(
action.x,
action.y,
action.distance,
@@ -353,7 +363,7 @@ class PerformActionsUseCaseImpl(
}
is ActionData.Text -> {
- imeInputEventInjector.inputText(action.text)
+ keyMapperImeMessenger.inputText(action.text)
result = Success(Unit)
}
@@ -483,42 +493,42 @@ class PerformActionsUseCaseImpl(
}
is ActionData.Volume.CycleRingerMode -> {
- result = when (volumeAdapter.ringerMode) {
- RingerMode.NORMAL -> volumeAdapter.setRingerMode(RingerMode.VIBRATE)
- RingerMode.VIBRATE -> volumeAdapter.setRingerMode(RingerMode.SILENT)
- RingerMode.SILENT -> volumeAdapter.setRingerMode(RingerMode.NORMAL)
+ result = when (audioAdapter.ringerMode) {
+ RingerMode.NORMAL -> audioAdapter.setRingerMode(RingerMode.VIBRATE)
+ RingerMode.VIBRATE -> audioAdapter.setRingerMode(RingerMode.SILENT)
+ RingerMode.SILENT -> audioAdapter.setRingerMode(RingerMode.NORMAL)
}
}
is ActionData.Volume.CycleVibrateRing -> {
- result = when (volumeAdapter.ringerMode) {
- RingerMode.NORMAL -> volumeAdapter.setRingerMode(RingerMode.VIBRATE)
- RingerMode.VIBRATE -> volumeAdapter.setRingerMode(RingerMode.NORMAL)
- RingerMode.SILENT -> volumeAdapter.setRingerMode(RingerMode.NORMAL)
+ result = when (audioAdapter.ringerMode) {
+ RingerMode.NORMAL -> audioAdapter.setRingerMode(RingerMode.VIBRATE)
+ RingerMode.VIBRATE -> audioAdapter.setRingerMode(RingerMode.NORMAL)
+ RingerMode.SILENT -> audioAdapter.setRingerMode(RingerMode.NORMAL)
}
}
is ActionData.DoNotDisturb.Disable -> {
- result = volumeAdapter.disableDndMode()
+ result = audioAdapter.disableDndMode()
}
is ActionData.StatusBar.ExpandNotifications -> {
val globalAction = AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS
- result = accessibilityService.doGlobalAction(globalAction).otherwise {
- shellAdapter.execute("cmd statusbar expand-notifications")
+ result = service.doGlobalAction(globalAction).otherwise {
+ shell.execute("cmd statusbar expand-notifications")
}
}
is ActionData.StatusBar.ToggleNotifications -> {
result =
- if (accessibilityService.rootNode?.packageName == "com.android.systemui") {
+ if (service.rootNode?.packageName == "com.android.systemui") {
closeStatusBarShade()
} else {
val globalAction = AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS
- accessibilityService.doGlobalAction(globalAction).otherwise {
- shellAdapter.execute("cmd statusbar expand-notifications")
+ service.doGlobalAction(globalAction).otherwise {
+ shell.execute("cmd statusbar expand-notifications")
}
}
}
@@ -527,20 +537,20 @@ class PerformActionsUseCaseImpl(
val globalAction = AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS
result =
- accessibilityService.doGlobalAction(globalAction).otherwise {
- shellAdapter.execute("cmd statusbar expand-settings")
+ service.doGlobalAction(globalAction).otherwise {
+ shell.execute("cmd statusbar expand-settings")
}
}
is ActionData.StatusBar.ToggleQuickSettings -> {
result =
- if (accessibilityService.rootNode?.packageName == "com.android.systemui") {
+ if (service.rootNode?.packageName == "com.android.systemui") {
closeStatusBarShade()
} else {
val globalAction = AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS
- accessibilityService.doGlobalAction(globalAction).otherwise {
- shellAdapter.execute("cmd statusbar expand-settings")
+ service.doGlobalAction(globalAction).otherwise {
+ shell.execute("cmd statusbar expand-settings")
}
}
}
@@ -591,32 +601,32 @@ class PerformActionsUseCaseImpl(
is ActionData.GoBack -> {
result =
- accessibilityService.doGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK)
+ service.doGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK)
}
is ActionData.GoHome -> {
result =
- accessibilityService.doGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME)
+ service.doGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME)
}
is ActionData.OpenRecents -> {
result =
- accessibilityService.doGlobalAction(AccessibilityService.GLOBAL_ACTION_RECENTS)
+ service.doGlobalAction(AccessibilityService.GLOBAL_ACTION_RECENTS)
}
is ActionData.ToggleSplitScreen -> {
result = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- accessibilityService.doGlobalAction(AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN)
+ service.doGlobalAction(AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN)
} else {
- Error.SdkVersionTooLow(minSdk = Build.VERSION_CODES.N)
+ KMError.SdkVersionTooLow(minSdk = Build.VERSION_CODES.N)
}
}
is ActionData.GoLastApp -> {
- accessibilityService.doGlobalAction(AccessibilityService.GLOBAL_ACTION_RECENTS)
+ service.doGlobalAction(AccessibilityService.GLOBAL_ACTION_RECENTS)
delay(100)
result =
- accessibilityService.doGlobalAction(AccessibilityService.GLOBAL_ACTION_RECENTS)
+ service.doGlobalAction(AccessibilityService.GLOBAL_ACTION_RECENTS)
}
is ActionData.OpenMenu -> {
@@ -648,30 +658,30 @@ class PerformActionsUseCaseImpl(
if (inputKeyEventsWithShizuku.value) {
shizukuInputEventInjector.inputKeyEvent(keyModel)
} else {
- imeInputEventInjector.inputKeyEvent(keyModel)
+ keyMapperImeMessenger.inputKeyEvent(keyModel)
}
result = Success(Unit)
}
is ActionData.ToggleKeyboard -> {
- val isHidden = accessibilityService.isKeyboardHidden.firstBlocking()
+ val isHidden = service.isKeyboardHidden.firstBlocking()
if (isHidden) {
- accessibilityService.showKeyboard()
+ service.showKeyboard()
} else {
- accessibilityService.hideKeyboard()
+ service.hideKeyboard()
}
result = Success(Unit)
}
is ActionData.ShowKeyboard -> {
- accessibilityService.showKeyboard()
+ service.showKeyboard()
result = Success(Unit)
}
is ActionData.HideKeyboard -> {
- accessibilityService.hideKeyboard()
+ service.hideKeyboard()
result = Success(Unit)
}
@@ -680,25 +690,25 @@ class PerformActionsUseCaseImpl(
}
is ActionData.CutText -> {
- result = accessibilityService.performActionOnNode({ it.isFocused }) {
+ result = service.performActionOnNode({ it.isFocused }) {
AccessibilityNodeAction(AccessibilityNodeInfo.ACTION_CUT)
}
}
is ActionData.CopyText -> {
- result = accessibilityService.performActionOnNode({ it.isFocused }) {
+ result = service.performActionOnNode({ it.isFocused }) {
AccessibilityNodeAction(AccessibilityNodeInfo.ACTION_COPY)
}
}
is ActionData.PasteText -> {
- result = accessibilityService.performActionOnNode({ it.isFocused }) {
+ result = service.performActionOnNode({ it.isFocused }) {
AccessibilityNodeAction(AccessibilityNodeInfo.ACTION_PASTE)
}
}
is ActionData.SelectWordAtCursor -> {
- result = accessibilityService.performActionOnNode({ it.isFocused }) { node ->
+ result = service.performActionOnNode({ it.isFocused }) { node ->
// it is at the cursor position if they both return the same value
if (node.textSelectionStart == node.textSelectionEnd) {
val cursorPosition = node.textSelectionStart
@@ -752,7 +762,7 @@ class PerformActionsUseCaseImpl(
// Wait 3 seconds so the message isn't shown in the screenshot.
delay(3000)
- popupMessageAdapter.showPopupMessage(
+ toastAdapter.show(
resourceProvider.getString(
R.string.toast_screenshot_taken,
),
@@ -760,7 +770,7 @@ class PerformActionsUseCaseImpl(
}
} else {
result =
- accessibilityService.doGlobalAction(AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT)
+ service.doGlobalAction(AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT)
}
}
@@ -780,7 +790,7 @@ class PerformActionsUseCaseImpl(
result = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
suAdapter.execute("input keyevent ${KeyEvent.KEYCODE_POWER}")
} else {
- accessibilityService.doGlobalAction(AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN)
+ service.doGlobalAction(AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN)
}
}
@@ -802,19 +812,21 @@ class PerformActionsUseCaseImpl(
is ActionData.ShowPowerMenu -> {
result =
- accessibilityService.doGlobalAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG)
+ service.doGlobalAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG)
}
is ActionData.Volume.ShowDialog -> {
- result = volumeAdapter.showVolumeUi()
+ result = audioAdapter.showVolumeUi()
}
ActionData.DismissAllNotifications -> {
- result = notificationReceiverAdapter.send(ServiceEvent.DismissAllNotifications)
+ result =
+ notificationReceiverAdapter.send(NotificationServiceEvent.DismissAllNotifications)
}
ActionData.DismissLastNotification -> {
- result = notificationReceiverAdapter.send(ServiceEvent.DismissLastNotification)
+ result =
+ notificationReceiverAdapter.send(NotificationServiceEvent.DismissLastNotification)
}
ActionData.AnswerCall -> {
@@ -845,22 +857,22 @@ class PerformActionsUseCaseImpl(
}
is ActionData.InteractUiElement -> {
- if (accessibilityService.activeWindowPackage.first() != action.packageName) {
- result = Error.UiElementNotFound
+ if (service.activeWindowPackage.first() != action.packageName) {
+ result = KMError.UiElementNotFound
} else {
- result = accessibilityService.performActionOnNode(
+ result = service.performActionOnNode(
findNode = { node ->
matchAccessibilityNode(node, action)
},
performAction = { AccessibilityNodeAction(action = action.nodeAction.accessibilityActionId) },
- ).otherwise { Error.UiElementNotFound }
+ ).otherwise { KMError.UiElementNotFound }
}
}
}
when (result) {
is Success -> Timber.d("Performed action $action, input event type: $inputEventType, key meta state: $keyMetaState")
- is Error -> Timber.d(
+ is KMError -> Timber.d(
"Failed to perform action $action, reason: ${result.getFullMessage(resourceProvider)}, action: $action, input event type: $inputEventType, key meta state: $keyMetaState",
)
}
@@ -869,21 +881,21 @@ class PerformActionsUseCaseImpl(
}
override fun getErrorSnapshot(): ActionErrorSnapshot {
- return getActionError.actionErrorSnapshot.firstBlocking()
+ return getActionErrorUseCase.actionErrorSnapshot.firstBlocking()
}
override val defaultRepeatDelay: Flow =
- preferenceRepository.get(Keys.defaultRepeatDelay)
+ settingsRepository.get(Keys.defaultRepeatDelay)
.map { it ?: PreferenceDefaults.REPEAT_DELAY }
.map { it.toLong() }
override val defaultRepeatRate: Flow =
- preferenceRepository.get(Keys.defaultRepeatRate)
+ settingsRepository.get(Keys.defaultRepeatRate)
.map { it ?: PreferenceDefaults.REPEAT_RATE }
.map { it.toLong() }
override val defaultHoldDownDuration: Flow =
- preferenceRepository.get(Keys.defaultHoldDownDuration)
+ settingsRepository.get(Keys.defaultHoldDownDuration)
.map { it ?: PreferenceDefaults.HOLD_DOWN_DURATION }
.map { it.toLong() }
@@ -892,7 +904,7 @@ class PerformActionsUseCaseImpl(
// automatically select a game controller as the input device for game controller key events
if (InputEventUtils.isGamepadKeyCode(action.keyCode)) {
- deviceAdapter.connectedInputDevices.value.ifIsData { inputDevices ->
+ devicesAdapter.connectedInputDevices.value.ifIsData { inputDevices ->
val device = inputDevices.find { it.isGameController }
if (device != null) {
@@ -904,7 +916,7 @@ class PerformActionsUseCaseImpl(
return 0
}
- val inputDevices = deviceAdapter.connectedInputDevices.value
+ val inputDevices = devicesAdapter.connectedInputDevices.value
val devicesWithSameDescriptor =
inputDevices.dataOrNull()
@@ -924,7 +936,7 @@ class PerformActionsUseCaseImpl(
code. if none do then use the first one
*/
val deviceThatHasKey = devicesWithSameDescriptor.singleOrNull {
- deviceAdapter.deviceHasKey(it.id, action.keyCode)
+ devicesAdapter.deviceHasKey(it.id, action.keyCode)
}
val device = deviceThatHasKey
@@ -934,18 +946,18 @@ class PerformActionsUseCaseImpl(
return device.id
}
- private fun closeStatusBarShade(): Result<*> {
+ private fun closeStatusBarShade(): KMResult<*> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- return accessibilityService
+ return service
.doGlobalAction(AccessibilityService.GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE)
} else {
- return shellAdapter.execute("cmd statusbar collapse")
+ return shell.execute("cmd statusbar collapse")
}
}
- private fun Result<*>.showErrorMessageOnFail() {
+ private fun KMResult<*>.showErrorMessageOnFail() {
onFailure {
- popupMessageAdapter.showPopupMessage(it.getFullMessage(resourceProvider))
+ toastAdapter.show(it.getFullMessage(resourceProvider))
}
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/RepeatMode.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/RepeatMode.kt
similarity index 52%
rename from app/src/main/java/io/github/sds100/keymapper/actions/RepeatMode.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/RepeatMode.kt
index 5a6abc9631..e9eeb2ae76 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/RepeatMode.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/RepeatMode.kt
@@ -1,8 +1,5 @@
-package io.github.sds100.keymapper.actions
+package io.github.sds100.keymapper.base.actions
-/**
- * Created by sds100 on 15/05/2021.
- */
enum class RepeatMode {
TRIGGER_RELEASED,
LIMIT_REACHED,
diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/TestActionEvent.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/TestActionEvent.kt
new file mode 100644
index 0000000000..f6e061950d
--- /dev/null
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/TestActionEvent.kt
@@ -0,0 +1,7 @@
+package io.github.sds100.keymapper.base.actions
+
+import io.github.sds100.keymapper.system.accessibility.AccessibilityServiceEvent
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class TestActionEvent(val action: ActionData) : AccessibilityServiceEvent()
diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/TestActionUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/TestActionUseCase.kt
new file mode 100644
index 0000000000..5459f60f53
--- /dev/null
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/TestActionUseCase.kt
@@ -0,0 +1,15 @@
+package io.github.sds100.keymapper.base.actions
+
+import io.github.sds100.keymapper.common.utils.KMResult
+import io.github.sds100.keymapper.system.accessibility.AccessibilityServiceAdapter
+import javax.inject.Inject
+
+class TestActionUseCaseImpl @Inject constructor(
+ private val serviceAdapter: AccessibilityServiceAdapter,
+) : TestActionUseCase {
+ override suspend fun invoke(action: ActionData): KMResult<*> = serviceAdapter.send(TestActionEvent(action))
+}
+
+interface TestActionUseCase {
+ suspend operator fun invoke(action: ActionData): KMResult<*>
+}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/keyevent/ChooseKeyCodeFragment.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/ChooseKeyCodeFragment.kt
similarity index 70%
rename from app/src/main/java/io/github/sds100/keymapper/actions/keyevent/ChooseKeyCodeFragment.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/ChooseKeyCodeFragment.kt
index 06ef9ba635..20ee488091 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/keyevent/ChooseKeyCodeFragment.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/ChooseKeyCodeFragment.kt
@@ -1,24 +1,21 @@
-package io.github.sds100.keymapper.actions.keyevent
+package io.github.sds100.keymapper.base.actions.keyevent
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.navigation.fragment.navArgs
import com.airbnb.epoxy.EpoxyRecyclerView
-import io.github.sds100.keymapper.databinding.FragmentSimpleRecyclerviewBinding
-import io.github.sds100.keymapper.simple
-import io.github.sds100.keymapper.util.Inject
-import io.github.sds100.keymapper.util.State
-import io.github.sds100.keymapper.util.launchRepeatOnLifecycle
-import io.github.sds100.keymapper.util.ui.RecyclerViewUtils
-import io.github.sds100.keymapper.util.ui.SimpleListItemOld
-import io.github.sds100.keymapper.util.ui.SimpleRecyclerViewFragment
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.sds100.keymapper.base.databinding.FragmentSimpleRecyclerviewBinding
+import io.github.sds100.keymapper.base.simple
+import io.github.sds100.keymapper.base.utils.ui.RecyclerViewUtils
+import io.github.sds100.keymapper.base.utils.ui.SimpleListItemOld
+import io.github.sds100.keymapper.base.utils.ui.SimpleRecyclerViewFragment
+import io.github.sds100.keymapper.base.utils.ui.launchRepeatOnLifecycle
+import io.github.sds100.keymapper.common.utils.State
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
-/**
- * Created by sds100 on 30/03/2020.
- */
-
+@AndroidEntryPoint
class ChooseKeyCodeFragment : SimpleRecyclerViewFragment() {
companion object {
const val EXTRA_KEYCODE = "extra_keycode"
@@ -28,9 +25,8 @@ class ChooseKeyCodeFragment : SimpleRecyclerViewFragment() {
override var searchStateKey: String? = SEARCH_STATE_KEY
private val args: ChooseKeyCodeFragmentArgs by navArgs()
- private val viewModel: ChooseKeyCodeViewModel by viewModels {
- Inject.chooseKeyCodeViewModel()
- }
+
+ private val viewModel: ChooseKeyCodeViewModel by viewModels()
override val listItems: Flow>>
get() = viewModel.state
@@ -40,7 +36,7 @@ class ChooseKeyCodeFragment : SimpleRecyclerViewFragment() {
RecyclerViewUtils.applySimpleListItemDecorations(binding.epoxyRecyclerView)
- viewLifecycleOwner.launchRepeatOnLifecycle(Lifecycle.State.RESUMED) {
+ viewLifecycleOwner.launchRepeatOnLifecycle(Lifecycle.State.CREATED) {
viewModel.returnResult.collectLatest {
returnResult(EXTRA_KEYCODE to it)
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/keyevent/ChooseKeyCodeViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/ChooseKeyCodeViewModel.kt
similarity index 75%
rename from app/src/main/java/io/github/sds100/keymapper/actions/keyevent/ChooseKeyCodeViewModel.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/ChooseKeyCodeViewModel.kt
index d6eed56979..67987c742a 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/keyevent/ChooseKeyCodeViewModel.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/ChooseKeyCodeViewModel.kt
@@ -1,14 +1,14 @@
-package io.github.sds100.keymapper.actions.keyevent
+package io.github.sds100.keymapper.base.actions.keyevent
import android.view.KeyEvent
import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
+import dagger.hilt.android.lifecycle.HiltViewModel
+import io.github.sds100.keymapper.base.utils.filterByQuery
+import io.github.sds100.keymapper.base.utils.ui.DefaultSimpleListItem
+import io.github.sds100.keymapper.base.utils.ui.SimpleListItemOld
+import io.github.sds100.keymapper.common.utils.State
import io.github.sds100.keymapper.system.inputevents.InputEventUtils
-import io.github.sds100.keymapper.util.State
-import io.github.sds100.keymapper.util.filterByQuery
-import io.github.sds100.keymapper.util.ui.DefaultSimpleListItem
-import io.github.sds100.keymapper.util.ui.SimpleListItemOld
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -19,12 +19,10 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import javax.inject.Inject
-/**
- * Created by sds100 on 31/03/2020.
- */
-
-class ChooseKeyCodeViewModel : ViewModel() {
+@HiltViewModel
+class ChooseKeyCodeViewModel @Inject constructor() : ViewModel() {
val searchQuery = MutableStateFlow(null)
@@ -67,10 +65,4 @@ class ChooseKeyCodeViewModel : ViewModel() {
_returnResult.emit(id.toInt())
}
}
-
- @Suppress("UNCHECKED_CAST")
- class Factory : ViewModelProvider.NewInstanceFactory() {
-
- override fun create(modelClass: Class): T = ChooseKeyCodeViewModel() as T
- }
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/keyevent/ConfigKeyEventActionFragment.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/ConfigKeyEventActionFragment.kt
similarity index 89%
rename from app/src/main/java/io/github/sds100/keymapper/actions/keyevent/ConfigKeyEventActionFragment.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/ConfigKeyEventActionFragment.kt
index 1f6dfcf81d..4f2b33a30b 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/keyevent/ConfigKeyEventActionFragment.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/ConfigKeyEventActionFragment.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions.keyevent
+package io.github.sds100.keymapper.base.actions.keyevent
import android.os.Bundle
import android.view.LayoutInflater
@@ -16,21 +16,18 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.databinding.FragmentConfigKeyEventBinding
-import io.github.sds100.keymapper.ui.utils.putJsonSerializable
-import io.github.sds100.keymapper.util.Inject
-import io.github.sds100.keymapper.util.launchRepeatOnLifecycle
-import io.github.sds100.keymapper.util.str
-import io.github.sds100.keymapper.util.ui.configuredCheckBox
-import io.github.sds100.keymapper.util.ui.setupNavigation
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.databinding.FragmentConfigKeyEventBinding
+import io.github.sds100.keymapper.base.utils.navigation.setupFragmentNavigation
+import io.github.sds100.keymapper.base.utils.ui.configuredCheckBox
+import io.github.sds100.keymapper.base.utils.ui.launchRepeatOnLifecycle
+import io.github.sds100.keymapper.base.utils.ui.str
+import io.github.sds100.keymapper.common.utils.putJsonSerializable
import kotlinx.coroutines.flow.collectLatest
import kotlinx.serialization.json.Json
-/**
- * Created by sds100 on 30/03/2020.
- */
-
+@AndroidEntryPoint
class ConfigKeyEventActionFragment : Fragment() {
companion object {
const val EXTRA_RESULT = "extra_result"
@@ -40,9 +37,7 @@ class ConfigKeyEventActionFragment : Fragment() {
private val requestKey: String by lazy { args.requestKey }
- private val viewModel: ConfigKeyEventActionViewModel by viewModels {
- Inject.configKeyEventViewModel(requireContext())
- }
+ private val viewModel: ConfigKeyEventActionViewModel by viewModels()
/**
* Scoped to the lifecycle of the fragment's view (between onCreateView and onDestroyView)
@@ -62,7 +57,7 @@ class ConfigKeyEventActionFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- viewModel.setupNavigation(this)
+ viewModel.setupFragmentNavigation(this)
if (args.keyEventAction != null) {
viewModel.loadAction(Json.decodeFromString(args.keyEventAction!!))
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/keyevent/ConfigKeyEventActionViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/ConfigKeyEventActionViewModel.kt
similarity index 78%
rename from app/src/main/java/io/github/sds100/keymapper/actions/keyevent/ConfigKeyEventActionViewModel.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/ConfigKeyEventActionViewModel.kt
index 30d1236f18..02fd7545a0 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/keyevent/ConfigKeyEventActionViewModel.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/ConfigKeyEventActionViewModel.kt
@@ -1,30 +1,33 @@
-package io.github.sds100.keymapper.actions.keyevent
+package io.github.sds100.keymapper.base.actions.keyevent
import android.annotation.SuppressLint
import android.view.KeyEvent
import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.actions.ActionData
+import dagger.hilt.android.lifecycle.HiltViewModel
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.actions.ActionData
+import io.github.sds100.keymapper.base.utils.InputEventStrings
+import io.github.sds100.keymapper.base.utils.getFullMessage
+import io.github.sds100.keymapper.base.utils.navigation.NavDestination
+import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider
+import io.github.sds100.keymapper.base.utils.navigation.NavigationProviderImpl
+import io.github.sds100.keymapper.base.utils.navigation.navigate
+import io.github.sds100.keymapper.base.utils.ui.CheckBoxListItem
+import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
+import io.github.sds100.keymapper.common.utils.KMError
+import io.github.sds100.keymapper.common.utils.KMResult
+import io.github.sds100.keymapper.common.utils.Success
+import io.github.sds100.keymapper.common.utils.errorOrNull
+import io.github.sds100.keymapper.common.utils.handle
+import io.github.sds100.keymapper.common.utils.hasFlag
+import io.github.sds100.keymapper.common.utils.isSuccess
+import io.github.sds100.keymapper.common.utils.minusFlag
+import io.github.sds100.keymapper.common.utils.success
+import io.github.sds100.keymapper.common.utils.valueOrNull
+import io.github.sds100.keymapper.common.utils.withFlag
import io.github.sds100.keymapper.system.devices.InputDeviceInfo
import io.github.sds100.keymapper.system.devices.InputDeviceUtils
-import io.github.sds100.keymapper.system.inputevents.InputEventUtils
-import io.github.sds100.keymapper.util.Error
-import io.github.sds100.keymapper.util.Result
-import io.github.sds100.keymapper.util.Success
-import io.github.sds100.keymapper.util.errorOrNull
-import io.github.sds100.keymapper.util.getFullMessage
-import io.github.sds100.keymapper.util.handle
-import io.github.sds100.keymapper.util.isSuccess
-import io.github.sds100.keymapper.util.success
-import io.github.sds100.keymapper.util.ui.CheckBoxListItem
-import io.github.sds100.keymapper.util.ui.NavDestination
-import io.github.sds100.keymapper.util.ui.NavigationViewModel
-import io.github.sds100.keymapper.util.ui.NavigationViewModelImpl
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-import io.github.sds100.keymapper.util.ui.navigate
-import io.github.sds100.keymapper.util.valueOrNull
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -34,20 +37,15 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
-import splitties.bitflags.hasFlag
-import splitties.bitflags.minusFlag
-import splitties.bitflags.withFlag
+import javax.inject.Inject
-/**
- * Created by sds100 on 30/03/2020.
- */
-
-class ConfigKeyEventActionViewModel(
+@HiltViewModel
+class ConfigKeyEventActionViewModel @Inject constructor(
private val useCase: ConfigKeyEventUseCase,
- resourceProvider: ResourceProvider,
+ private val resourceProvider: ResourceProvider,
) : ViewModel(),
ResourceProvider by resourceProvider,
- NavigationViewModel by NavigationViewModelImpl() {
+ NavigationProvider by NavigationProviderImpl() {
private val keyEventState = MutableStateFlow(KeyEventState())
@@ -115,8 +113,8 @@ class ConfigKeyEventActionViewModel(
fun onKeyCodeTextChanged(text: String) {
val keyCodeState = when {
- text.isBlank() -> Error.EmptyText
- text.toIntOrNull() == null -> Error.InvalidNumber
+ text.isBlank() -> KMError.EmptyText
+ text.toIntOrNull() == null -> KMError.InvalidNumber
else -> text.toInt().success()
}
@@ -192,7 +190,7 @@ class ConfigKeyEventActionViewModel(
onError = { "" },
)
- val modifierListItems = InputEventUtils.MODIFIER_LABELS.map { (modifier, label) ->
+ val modifierListItems = InputEventStrings.MODIFIER_LABELS.map { (modifier, label) ->
CheckBoxListItem(
id = modifier.toString(),
label = getString(label),
@@ -238,17 +236,8 @@ class ConfigKeyEventActionViewModel(
)
}
- @Suppress("UNCHECKED_CAST")
- class Factory(
- private val useCase: ConfigKeyEventUseCase,
- private val resourceProvider: ResourceProvider,
- ) : ViewModelProvider.NewInstanceFactory() {
-
- override fun create(modelClass: Class): T = ConfigKeyEventActionViewModel(useCase, resourceProvider) as T
- }
-
private data class KeyEventState(
- val keyCode: Result = Error.EmptyText,
+ val keyCode: KMResult = KMError.EmptyText,
val chosenDevice: InputDeviceInfo? = null,
val useShell: Boolean = false,
val metaState: Int = 0,
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/keyevent/ConfigKeyEventUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/ConfigKeyEventUseCase.kt
similarity index 83%
rename from app/src/main/java/io/github/sds100/keymapper/actions/keyevent/ConfigKeyEventUseCase.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/ConfigKeyEventUseCase.kt
index a5c13e898a..a304220fab 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/keyevent/ConfigKeyEventUseCase.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/ConfigKeyEventUseCase.kt
@@ -1,18 +1,15 @@
-package io.github.sds100.keymapper.actions.keyevent
+package io.github.sds100.keymapper.base.actions.keyevent
+import io.github.sds100.keymapper.common.utils.State
import io.github.sds100.keymapper.data.Keys
import io.github.sds100.keymapper.data.repositories.PreferenceRepository
import io.github.sds100.keymapper.system.devices.DevicesAdapter
import io.github.sds100.keymapper.system.devices.InputDeviceInfo
-import io.github.sds100.keymapper.util.State
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
+import javax.inject.Inject
-/**
- * Created by sds100 on 01/05/2021.
- */
-
-class ConfigKeyEventUseCaseImpl(
+class ConfigKeyEventUseCaseImpl @Inject constructor(
private val preferenceRepository: PreferenceRepository,
private val devicesAdapter: DevicesAdapter,
) : ConfigKeyEventUseCase {
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/pinchscreen/PinchPickCoordinateResult.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/pinchscreen/PinchPickCoordinateResult.kt
similarity index 68%
rename from app/src/main/java/io/github/sds100/keymapper/actions/pinchscreen/PinchPickCoordinateResult.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/pinchscreen/PinchPickCoordinateResult.kt
index b790c293b1..b3624183d4 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/pinchscreen/PinchPickCoordinateResult.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/pinchscreen/PinchPickCoordinateResult.kt
@@ -1,12 +1,8 @@
-package io.github.sds100.keymapper.actions.pinchscreen
+package io.github.sds100.keymapper.base.actions.pinchscreen
+import io.github.sds100.keymapper.common.utils.PinchScreenType
import kotlinx.serialization.Serializable
-enum class PinchScreenType {
- PINCH_IN,
- PINCH_OUT,
-}
-
@Serializable
data class PinchPickCoordinateResult(
val x: Int,
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/pinchscreen/PinchPickDisplayCoordinateFragment.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/pinchscreen/PinchPickDisplayCoordinateFragment.kt
similarity index 90%
rename from app/src/main/java/io/github/sds100/keymapper/actions/pinchscreen/PinchPickDisplayCoordinateFragment.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/pinchscreen/PinchPickDisplayCoordinateFragment.kt
index 654619d14b..8ed81bd516 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/pinchscreen/PinchPickDisplayCoordinateFragment.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/pinchscreen/PinchPickDisplayCoordinateFragment.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions.pinchscreen
+package io.github.sds100.keymapper.base.actions.pinchscreen
import android.annotation.SuppressLint
import android.graphics.Bitmap
@@ -21,17 +21,16 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.databinding.FragmentPinchPickCoordinatesBinding
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.databinding.FragmentPinchPickCoordinatesBinding
+import io.github.sds100.keymapper.base.utils.ui.launchRepeatOnLifecycle
+import io.github.sds100.keymapper.base.utils.ui.str
import io.github.sds100.keymapper.system.files.FileUtils
-import io.github.sds100.keymapper.util.Inject
-import io.github.sds100.keymapper.util.launchRepeatOnLifecycle
-import io.github.sds100.keymapper.util.str
-import io.github.sds100.keymapper.util.ui.showPopups
import kotlinx.coroutines.flow.collectLatest
-import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
+@AndroidEntryPoint
class PinchPickDisplayCoordinateFragment : Fragment() {
companion object {
const val EXTRA_RESULT = "extra_result"
@@ -41,9 +40,7 @@ class PinchPickDisplayCoordinateFragment : Fragment() {
private val requestKey: String by lazy { args.requestKey }
private var pinchTypesDisplayValues = mutableListOf()
- private val viewModel: PinchPickDisplayCoordinateViewModel by viewModels {
- Inject.pinchCoordinateActionTypeViewModel(requireContext())
- }
+ private val viewModel: PinchPickDisplayCoordinateViewModel by viewModels()
private val screenshotLauncher =
registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
@@ -113,7 +110,6 @@ class PinchPickDisplayCoordinateFragment : Fragment() {
android.R.layout.simple_spinner_dropdown_item,
pinchTypesDisplayValues,
)
- viewModel.showPopups(this, binding)
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
findNavController().navigateUp()
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/pinchscreen/PinchPickDisplayCoordinateViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/pinchscreen/PinchPickDisplayCoordinateViewModel.kt
similarity index 89%
rename from app/src/main/java/io/github/sds100/keymapper/actions/pinchscreen/PinchPickDisplayCoordinateViewModel.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/pinchscreen/PinchPickDisplayCoordinateViewModel.kt
index 5cff836fe5..4450f70f65 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/pinchscreen/PinchPickDisplayCoordinateViewModel.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/pinchscreen/PinchPickDisplayCoordinateViewModel.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions.pinchscreen
+package io.github.sds100.keymapper.base.actions.pinchscreen
import android.accessibilityservice.GestureDescription
import android.graphics.Bitmap
@@ -7,14 +7,14 @@ import android.os.Build
import android.view.View
import android.widget.AdapterView
import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.util.ui.PopupUi
-import io.github.sds100.keymapper.util.ui.PopupViewModel
-import io.github.sds100.keymapper.util.ui.PopupViewModelImpl
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-import io.github.sds100.keymapper.util.ui.showPopup
+import dagger.hilt.android.lifecycle.HiltViewModel
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.utils.ui.DialogModel
+import io.github.sds100.keymapper.base.utils.ui.DialogProvider
+import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
+import io.github.sds100.keymapper.base.utils.ui.showDialog
+import io.github.sds100.keymapper.common.utils.PinchScreenType
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -25,13 +25,16 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
+import javax.inject.Inject
import kotlin.math.roundToInt
-class PinchPickDisplayCoordinateViewModel(
+@HiltViewModel
+class PinchPickDisplayCoordinateViewModel @Inject constructor(
resourceProvider: ResourceProvider,
+ dialogProvider: DialogProvider,
) : ViewModel(),
ResourceProvider by resourceProvider,
- PopupViewModel by PopupViewModelImpl() {
+ DialogProvider by dialogProvider {
private val pinchTypes = arrayOf(PinchScreenType.PINCH_IN.name, PinchScreenType.PINCH_OUT.name)
@@ -160,11 +163,11 @@ class PinchPickDisplayCoordinateViewModel(
(displaySize.y != newBitmap.width && displaySize.x != newBitmap.height)
) {
viewModelScope.launch {
- val snackBar = PopupUi.SnackBar(
+ val snackBar = DialogModel.SnackBar(
message = getString(R.string.toast_incorrect_screenshot_resolution),
)
- showPopup("incorrect_resolution", snackBar)
+ showDialog("incorrect_resolution", snackBar)
}
return
@@ -224,9 +227,9 @@ class PinchPickDisplayCoordinateViewModel(
val fingerCount = fingerCount.value ?: return@launch
val duration = duration.value ?: return@launch
- val description = showPopup(
+ val description = showDialog(
"coordinate_description",
- PopupUi.Text(
+ DialogModel.Text(
getString(R.string.hint_tap_coordinate_title),
allowEmpty = true,
text = description.value ?: "",
@@ -269,13 +272,4 @@ class PinchPickDisplayCoordinateViewModel(
super.onCleared()
}
-
- @Suppress("UNCHECKED_CAST")
- class Factory(
- private val resourceProvider: ResourceProvider,
- ) : ViewModelProvider.NewInstanceFactory() {
-
- override fun create(modelClass: Class): T =
- PinchPickDisplayCoordinateViewModel(resourceProvider) as T
- }
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/sound/ChooseSoundFileFragment.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/sound/ChooseSoundFileFragment.kt
similarity index 91%
rename from app/src/main/java/io/github/sds100/keymapper/actions/sound/ChooseSoundFileFragment.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/sound/ChooseSoundFileFragment.kt
index 9584e52bd0..ab1088467b 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/sound/ChooseSoundFileFragment.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/sound/ChooseSoundFileFragment.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions.sound
+package io.github.sds100.keymapper.base.actions.sound
import android.app.Activity
import android.content.Intent
@@ -21,21 +21,17 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
-import io.github.sds100.keymapper.databinding.FragmentChooseSoundFileBinding
-import io.github.sds100.keymapper.simple
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.sds100.keymapper.base.databinding.FragmentChooseSoundFileBinding
+import io.github.sds100.keymapper.base.simple
+import io.github.sds100.keymapper.base.utils.ui.launchRepeatOnLifecycle
import io.github.sds100.keymapper.system.files.FileUtils
-import io.github.sds100.keymapper.util.Inject
-import io.github.sds100.keymapper.util.launchRepeatOnLifecycle
-import io.github.sds100.keymapper.util.ui.showPopups
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.update
import kotlinx.serialization.json.Json
-/**
- * Created by sds100 on 22/06/2021.
- */
-
+@AndroidEntryPoint
class ChooseSoundFileFragment : Fragment() {
companion object {
const val EXTRA_RESULT = "extra_sound_file_result"
@@ -44,9 +40,7 @@ class ChooseSoundFileFragment : Fragment() {
private val args: ChooseSoundFileFragmentArgs by navArgs()
private val requestKey: String by lazy { args.requestKey }
- private val viewModel: ChooseSoundFileViewModel by viewModels {
- Inject.soundFileActionTypeViewModel(requireContext())
- }
+ private val viewModel: ChooseSoundFileViewModel by viewModels()
private val chooseSoundFileLauncher =
registerForActivityResult(ActivityResultContracts.GetContent()) {
@@ -98,7 +92,6 @@ class ChooseSoundFileFragment : Fragment() {
}
binding.viewModel = viewModel
- viewModel.showPopups(this, binding)
viewLifecycleOwner.launchRepeatOnLifecycle(Lifecycle.State.RESUMED) {
viewModel.chooseSoundFile.collectLatest {
diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/sound/ChooseSoundFileUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/sound/ChooseSoundFileUseCase.kt
new file mode 100644
index 0000000000..7f6566006d
--- /dev/null
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/sound/ChooseSoundFileUseCase.kt
@@ -0,0 +1,37 @@
+package io.github.sds100.keymapper.base.actions.sound
+
+import io.github.sds100.keymapper.common.utils.KMError
+import io.github.sds100.keymapper.common.utils.KMResult
+import io.github.sds100.keymapper.common.utils.Success
+import io.github.sds100.keymapper.system.files.FileAdapter
+import kotlinx.coroutines.flow.StateFlow
+import javax.inject.Inject
+
+class ChooseSoundFileUseCaseImpl @Inject constructor(
+ private val fileAdapter: FileAdapter,
+ private val soundsManager: SoundsManager,
+) : ChooseSoundFileUseCase {
+ override val soundFiles = soundsManager.soundFiles
+
+ override suspend fun saveSound(uri: String): KMResult = soundsManager.saveNewSound(uri)
+
+ override fun getSoundFileName(uri: String): KMResult {
+ val name = fileAdapter.getFileFromUri(uri).name
+
+ return if (name == null) {
+ KMError.NoFileName
+ } else {
+ Success(name)
+ }
+ }
+}
+
+interface ChooseSoundFileUseCase {
+
+ /**
+ * @return the sound file uid
+ */
+ suspend fun saveSound(uri: String): KMResult
+ val soundFiles: StateFlow>
+ fun getSoundFileName(uri: String): KMResult
+}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/sound/ChooseSoundFileViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/sound/ChooseSoundFileViewModel.kt
similarity index 65%
rename from app/src/main/java/io/github/sds100/keymapper/actions/sound/ChooseSoundFileViewModel.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/sound/ChooseSoundFileViewModel.kt
index 4967227dd6..f858528742 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/sound/ChooseSoundFileViewModel.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/sound/ChooseSoundFileViewModel.kt
@@ -1,20 +1,19 @@
-package io.github.sds100.keymapper.actions.sound
+package io.github.sds100.keymapper.base.actions.sound
import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.actions.ActionData
-import io.github.sds100.keymapper.util.getFullMessage
-import io.github.sds100.keymapper.util.onFailure
-import io.github.sds100.keymapper.util.onSuccess
-import io.github.sds100.keymapper.util.ui.DefaultSimpleListItem
-import io.github.sds100.keymapper.util.ui.PopupUi
-import io.github.sds100.keymapper.util.ui.PopupViewModel
-import io.github.sds100.keymapper.util.ui.PopupViewModelImpl
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-import io.github.sds100.keymapper.util.ui.showPopup
-import io.github.sds100.keymapper.util.valueOrNull
+import dagger.hilt.android.lifecycle.HiltViewModel
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.actions.ActionData
+import io.github.sds100.keymapper.base.utils.getFullMessage
+import io.github.sds100.keymapper.base.utils.ui.DefaultSimpleListItem
+import io.github.sds100.keymapper.base.utils.ui.DialogModel
+import io.github.sds100.keymapper.base.utils.ui.DialogProvider
+import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
+import io.github.sds100.keymapper.base.utils.ui.showDialog
+import io.github.sds100.keymapper.common.utils.onFailure
+import io.github.sds100.keymapper.common.utils.onSuccess
+import io.github.sds100.keymapper.common.utils.valueOrNull
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -24,15 +23,15 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
+import javax.inject.Inject
-/**
- * Created by sds100 on 31/03/2020.
- */
-class ChooseSoundFileViewModel(
+@HiltViewModel
+class ChooseSoundFileViewModel @Inject constructor(
resourceProvider: ResourceProvider,
+ dialogProvider: DialogProvider,
private val useCase: ChooseSoundFileUseCase,
) : ViewModel(),
- PopupViewModel by PopupViewModelImpl(),
+ DialogProvider by dialogProvider,
ResourceProvider by resourceProvider {
private val _chooseSoundFile = MutableSharedFlow()
@@ -67,13 +66,13 @@ class ChooseSoundFileViewModel(
viewModelScope.launch {
val soundFileInfo = useCase.soundFiles.value.find { it.uid == id } ?: return@launch
- val dialog = PopupUi.Text(
+ val dialog = DialogModel.Text(
hint = getString(R.string.hint_sound_file_description),
allowEmpty = false,
text = soundFileInfo.name,
)
- val soundDescription = showPopup("file_description", dialog) ?: return@launch
+ val soundDescription = showDialog("file_description", dialog) ?: return@launch
returnResult.update {
ActionData.Sound.SoundFile(
@@ -88,13 +87,13 @@ class ChooseSoundFileViewModel(
viewModelScope.launch {
val fileName = useCase.getSoundFileName(uri).valueOrNull() ?: return@launch
- val dialog = PopupUi.Text(
+ val dialog = DialogModel.Text(
hint = getString(R.string.hint_sound_file_description),
allowEmpty = false,
text = fileName,
)
- val soundDescription = showPopup("file_description", dialog)
+ val soundDescription = showDialog("file_description", dialog)
soundDescription ?: return@launch
@@ -107,8 +106,8 @@ class ChooseSoundFileViewModel(
)
}
}.onFailure { error ->
- val toast = PopupUi.Toast(error.getFullMessage(this@ChooseSoundFileViewModel))
- showPopup("failed_toast", toast)
+ val toast = DialogModel.Toast(error.getFullMessage(this@ChooseSoundFileViewModel))
+ showDialog("failed_toast", toast)
}
}
}
@@ -118,13 +117,4 @@ class ChooseSoundFileViewModel(
returnResult.update { ActionData.Sound.Ringtone(uri) }
}
}
-
- @Suppress("UNCHECKED_CAST")
- class Factory(
- private val resourceProvider: ResourceProvider,
- private val useCase: ChooseSoundFileUseCase,
- ) : ViewModelProvider.NewInstanceFactory() {
-
- override fun create(modelClass: Class): T = ChooseSoundFileViewModel(resourceProvider, useCase) as T
- }
}
diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/sound/SoundFileInfo.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/sound/SoundFileInfo.kt
new file mode 100644
index 0000000000..a6faa7bf34
--- /dev/null
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/sound/SoundFileInfo.kt
@@ -0,0 +1,3 @@
+package io.github.sds100.keymapper.base.actions.sound
+
+data class SoundFileInfo(val uid: String, val name: String)
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/sound/SoundsManager.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/sound/SoundsManager.kt
similarity index 69%
rename from app/src/main/java/io/github/sds100/keymapper/actions/sound/SoundsManager.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/sound/SoundsManager.kt
index 8df6a65e38..a9ead62745 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/sound/SoundsManager.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/sound/SoundsManager.kt
@@ -1,25 +1,24 @@
-package io.github.sds100.keymapper.actions.sound
-
+package io.github.sds100.keymapper.base.actions.sound
+
+import io.github.sds100.keymapper.common.utils.KMError
+import io.github.sds100.keymapper.common.utils.KMResult
+import io.github.sds100.keymapper.common.utils.Success
+import io.github.sds100.keymapper.common.utils.onFailure
+import io.github.sds100.keymapper.common.utils.onSuccess
+import io.github.sds100.keymapper.common.utils.then
import io.github.sds100.keymapper.system.files.FileAdapter
import io.github.sds100.keymapper.system.files.IFile
-import io.github.sds100.keymapper.util.Error
-import io.github.sds100.keymapper.util.Result
-import io.github.sds100.keymapper.util.Success
-import io.github.sds100.keymapper.util.onFailure
-import io.github.sds100.keymapper.util.onSuccess
-import io.github.sds100.keymapper.util.then
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.UUID
+import javax.inject.Inject
+import javax.inject.Singleton
-/**
- * Created by sds100 on 24/06/2021.
- */
-
-class SoundsManagerImpl(
+@Singleton
+class SoundsManagerImpl @Inject constructor(
private val coroutineScope: CoroutineScope,
private val fileAdapter: FileAdapter,
) : SoundsManager {
@@ -35,7 +34,7 @@ class SoundsManagerImpl(
}
}
- override suspend fun saveNewSound(uri: String): Result {
+ override suspend fun saveNewSound(uri: String): KMResult {
val uid = UUID.randomUUID().toString()
val soundFile = fileAdapter.getFileFromUri(uri)
@@ -48,13 +47,13 @@ class SoundsManagerImpl(
.then { Success(uid) }
.onSuccess { updateSoundFilesFlow() }
.onFailure {
- if (it is Error.Exception) {
+ if (it is KMError.Exception) {
Timber.d(it.exception)
}
}
}
- override suspend fun restoreSound(file: IFile): Result<*> {
+ override suspend fun restoreSound(file: IFile): KMResult<*> {
val soundsDir = fileAdapter.getPrivateFile(SOUNDS_DIR_NAME)
soundsDir.createDirectory()
@@ -69,20 +68,20 @@ class SoundsManagerImpl(
}
}
- override fun getSound(uid: String): Result {
+ override fun getSound(uid: String): KMResult {
val soundsDir = fileAdapter.getPrivateFile(SOUNDS_DIR_NAME)
soundsDir.createDirectory()
val matchingFile = soundsDir.listFiles()!!.find { it.name?.contains(uid) == true }
if (matchingFile == null) {
- return Error.CantFindSoundFile
+ return KMError.CantFindSoundFile
} else {
return Success(matchingFile)
}
}
- override fun deleteSound(uid: String): Result<*> = getSound(uid)
+ override fun deleteSound(uid: String): KMResult<*> = getSound(uid)
.then { Success(it.delete()) }
.onSuccess { updateSoundFilesFlow() }
@@ -106,12 +105,11 @@ class SoundsManagerImpl(
.map { getSoundFileInfo(it.name!!) }
}
- private fun createSoundCopyFileName(originalSoundFile: IFile, uid: String): String =
- buildString {
- append(originalSoundFile.baseName)
- append("_$uid")
- append(".${originalSoundFile.extension}")
- }
+ private fun createSoundCopyFileName(originalSoundFile: IFile, uid: String): String = buildString {
+ append(originalSoundFile.baseName)
+ append("_$uid")
+ append(".${originalSoundFile.extension}")
+ }
}
interface SoundsManager {
@@ -120,8 +118,8 @@ interface SoundsManager {
/**
* @return the sound file uid
*/
- suspend fun saveNewSound(uri: String): Result
- suspend fun restoreSound(file: IFile): Result<*>
- fun getSound(uid: String): Result
- fun deleteSound(uid: String): Result<*>
+ suspend fun saveNewSound(uri: String): KMResult
+ suspend fun restoreSound(file: IFile): KMResult<*>
+ fun getSound(uid: String): KMResult
+ fun deleteSound(uid: String): KMResult<*>
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/swipescreen/SwipePickCoordinateResult.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/swipescreen/SwipePickCoordinateResult.kt
similarity index 81%
rename from app/src/main/java/io/github/sds100/keymapper/actions/swipescreen/SwipePickCoordinateResult.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/swipescreen/SwipePickCoordinateResult.kt
index 0efe4ec64e..7f084200d0 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/swipescreen/SwipePickCoordinateResult.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/swipescreen/SwipePickCoordinateResult.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions.swipescreen
+package io.github.sds100.keymapper.base.actions.swipescreen
import kotlinx.serialization.Serializable
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/swipescreen/SwipePickDisplayCoordinateFragment.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/swipescreen/SwipePickDisplayCoordinateFragment.kt
similarity index 91%
rename from app/src/main/java/io/github/sds100/keymapper/actions/swipescreen/SwipePickDisplayCoordinateFragment.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/swipescreen/SwipePickDisplayCoordinateFragment.kt
index cfebafa3a9..888ae4826f 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/swipescreen/SwipePickDisplayCoordinateFragment.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/swipescreen/SwipePickDisplayCoordinateFragment.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions.swipescreen
+package io.github.sds100.keymapper.base.actions.swipescreen
import android.annotation.SuppressLint
import android.graphics.Bitmap
@@ -20,15 +20,14 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
-import io.github.sds100.keymapper.databinding.FragmentSwipePickCoordinatesBinding
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.sds100.keymapper.base.databinding.FragmentSwipePickCoordinatesBinding
+import io.github.sds100.keymapper.base.utils.ui.launchRepeatOnLifecycle
import io.github.sds100.keymapper.system.files.FileUtils
-import io.github.sds100.keymapper.util.Inject
-import io.github.sds100.keymapper.util.launchRepeatOnLifecycle
-import io.github.sds100.keymapper.util.ui.showPopups
import kotlinx.coroutines.flow.collectLatest
-import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
+@AndroidEntryPoint
class SwipePickDisplayCoordinateFragment : Fragment() {
companion object {
const val EXTRA_RESULT = "extra_result"
@@ -37,9 +36,7 @@ class SwipePickDisplayCoordinateFragment : Fragment() {
private val args: SwipePickDisplayCoordinateFragmentArgs by navArgs()
private val requestKey: String by lazy { args.requestKey }
- private val viewModel: SwipePickDisplayCoordinateViewModel by viewModels {
- Inject.swipeCoordinateActionTypeViewModel(requireContext())
- }
+ private val viewModel: SwipePickDisplayCoordinateViewModel by viewModels()
private val screenshotLauncher =
registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
@@ -99,8 +96,6 @@ class SwipePickDisplayCoordinateFragment : Fragment() {
binding.viewModel = viewModel
- viewModel.showPopups(this, binding)
-
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
findNavController().navigateUp()
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/swipescreen/SwipePickDisplayCoordinateViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/swipescreen/SwipePickDisplayCoordinateViewModel.kt
similarity index 89%
rename from app/src/main/java/io/github/sds100/keymapper/actions/swipescreen/SwipePickDisplayCoordinateViewModel.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/swipescreen/SwipePickDisplayCoordinateViewModel.kt
index 6fbfe7cf64..c5afe9ad3f 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/swipescreen/SwipePickDisplayCoordinateViewModel.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/swipescreen/SwipePickDisplayCoordinateViewModel.kt
@@ -1,18 +1,17 @@
-package io.github.sds100.keymapper.actions.swipescreen
+package io.github.sds100.keymapper.base.actions.swipescreen
import android.accessibilityservice.GestureDescription
import android.graphics.Bitmap
import android.graphics.Point
import android.os.Build
import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.util.ui.PopupUi
-import io.github.sds100.keymapper.util.ui.PopupViewModel
-import io.github.sds100.keymapper.util.ui.PopupViewModelImpl
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-import io.github.sds100.keymapper.util.ui.showPopup
+import dagger.hilt.android.lifecycle.HiltViewModel
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.utils.ui.DialogModel
+import io.github.sds100.keymapper.base.utils.ui.DialogProvider
+import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
+import io.github.sds100.keymapper.base.utils.ui.showDialog
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -23,6 +22,7 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
+import javax.inject.Inject
import kotlin.math.roundToInt
enum class ScreenshotTouchType {
@@ -30,11 +30,13 @@ enum class ScreenshotTouchType {
END,
}
-class SwipePickDisplayCoordinateViewModel(
+@HiltViewModel
+class SwipePickDisplayCoordinateViewModel @Inject constructor(
resourceProvider: ResourceProvider,
+ dialogProvider: DialogProvider,
) : ViewModel(),
ResourceProvider by resourceProvider,
- PopupViewModel by PopupViewModelImpl() {
+ DialogProvider by dialogProvider {
val screenshotTouchTypeStart = ScreenshotTouchType.START
val screenshotTouchTypeEnd = ScreenshotTouchType.END
@@ -162,11 +164,11 @@ class SwipePickDisplayCoordinateViewModel(
(displaySize.y != newBitmap.width && displaySize.x != newBitmap.height)
) {
viewModelScope.launch {
- val snackBar = PopupUi.SnackBar(
+ val snackBar = DialogModel.SnackBar(
message = getString(R.string.toast_incorrect_screenshot_resolution),
)
- showPopup("incorrect_resolution", snackBar)
+ showDialog("incorrect_resolution", snackBar)
}
return
@@ -231,9 +233,9 @@ class SwipePickDisplayCoordinateViewModel(
val fingerCount = fingerCount.value ?: return@launch
val duration = duration.value ?: return@launch
- val description = showPopup(
+ val description = showDialog(
"coordinate_description",
- PopupUi.Text(
+ DialogModel.Text(
getString(R.string.hint_tap_coordinate_title),
allowEmpty = true,
text = description.value ?: "",
@@ -272,13 +274,4 @@ class SwipePickDisplayCoordinateViewModel(
super.onCleared()
}
-
- @Suppress("UNCHECKED_CAST")
- class Factory(
- private val resourceProvider: ResourceProvider,
- ) : ViewModelProvider.NewInstanceFactory() {
-
- override fun create(modelClass: Class): T =
- SwipePickDisplayCoordinateViewModel(resourceProvider) as T
- }
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/tapscreen/PickCoordinateImageView.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/tapscreen/PickCoordinateImageView.kt
similarity index 90%
rename from app/src/main/java/io/github/sds100/keymapper/actions/tapscreen/PickCoordinateImageView.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/tapscreen/PickCoordinateImageView.kt
index 04e72d5bdc..fcbd125cf2 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/tapscreen/PickCoordinateImageView.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/tapscreen/PickCoordinateImageView.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions.tapscreen
+package io.github.sds100.keymapper.base.actions.tapscreen
import android.content.Context
import android.graphics.Canvas
@@ -7,14 +7,11 @@ import android.graphics.Point
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.appcompat.widget.AppCompatImageView
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.util.color
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.utils.ui.color
import kotlinx.coroutines.flow.MutableStateFlow
import kotlin.math.roundToInt
-/**
- * Created by sds100 on 08/08/20.
- */
class PickCoordinateImageView(
context: Context,
attrs: AttributeSet?,
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/tapscreen/PickCoordinateResult.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/tapscreen/PickCoordinateResult.kt
similarity index 58%
rename from app/src/main/java/io/github/sds100/keymapper/actions/tapscreen/PickCoordinateResult.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/tapscreen/PickCoordinateResult.kt
index cdb05e20b3..8c45352d20 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/tapscreen/PickCoordinateResult.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/tapscreen/PickCoordinateResult.kt
@@ -1,9 +1,6 @@
-package io.github.sds100.keymapper.actions.tapscreen
+package io.github.sds100.keymapper.base.actions.tapscreen
import kotlinx.serialization.Serializable
-/**
- * Created by sds100 on 25/03/2021.
- */
@Serializable
data class PickCoordinateResult(val x: Int, val y: Int, val description: String)
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/tapscreen/PickDisplayCoordinateFragment.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/tapscreen/PickDisplayCoordinateFragment.kt
similarity index 90%
rename from app/src/main/java/io/github/sds100/keymapper/actions/tapscreen/PickDisplayCoordinateFragment.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/tapscreen/PickDisplayCoordinateFragment.kt
index 173e483c29..1de00aca0f 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/tapscreen/PickDisplayCoordinateFragment.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/tapscreen/PickDisplayCoordinateFragment.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions.tapscreen
+package io.github.sds100.keymapper.base.actions.tapscreen
import android.annotation.SuppressLint
import android.graphics.Bitmap
@@ -20,19 +20,14 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
-import io.github.sds100.keymapper.databinding.FragmentPickCoordinateBinding
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.sds100.keymapper.base.databinding.FragmentPickCoordinateBinding
+import io.github.sds100.keymapper.base.utils.ui.launchRepeatOnLifecycle
import io.github.sds100.keymapper.system.files.FileUtils
-import io.github.sds100.keymapper.util.Inject
-import io.github.sds100.keymapper.util.launchRepeatOnLifecycle
-import io.github.sds100.keymapper.util.ui.showPopups
import kotlinx.coroutines.flow.collectLatest
-import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
-/**
- * Created by sds100 on 30/03/2020.
- */
-
+@AndroidEntryPoint
class PickDisplayCoordinateFragment : Fragment() {
companion object {
const val EXTRA_RESULT = "extra_result"
@@ -41,9 +36,7 @@ class PickDisplayCoordinateFragment : Fragment() {
private val args: PickDisplayCoordinateFragmentArgs by navArgs()
private val requestKey: String by lazy { args.requestKey }
- private val viewModel: PickDisplayCoordinateViewModel by viewModels {
- Inject.tapCoordinateActionTypeViewModel(requireContext())
- }
+ private val viewModel: PickDisplayCoordinateViewModel by viewModels()
private val screenshotLauncher =
registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
@@ -103,8 +96,6 @@ class PickDisplayCoordinateFragment : Fragment() {
binding.viewModel = viewModel
- viewModel.showPopups(this, binding)
-
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
findNavController().navigateUp()
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/tapscreen/PickDisplayCoordinateViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/tapscreen/PickDisplayCoordinateViewModel.kt
similarity index 78%
rename from app/src/main/java/io/github/sds100/keymapper/actions/tapscreen/PickDisplayCoordinateViewModel.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/tapscreen/PickDisplayCoordinateViewModel.kt
index 98a8be7e09..86afc6dc0f 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/tapscreen/PickDisplayCoordinateViewModel.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/tapscreen/PickDisplayCoordinateViewModel.kt
@@ -1,16 +1,15 @@
-package io.github.sds100.keymapper.actions.tapscreen
+package io.github.sds100.keymapper.base.actions.tapscreen
import android.graphics.Bitmap
import android.graphics.Point
import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.util.ui.PopupUi
-import io.github.sds100.keymapper.util.ui.PopupViewModel
-import io.github.sds100.keymapper.util.ui.PopupViewModelImpl
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-import io.github.sds100.keymapper.util.ui.showPopup
+import dagger.hilt.android.lifecycle.HiltViewModel
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.utils.ui.DialogModel
+import io.github.sds100.keymapper.base.utils.ui.DialogProvider
+import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
+import io.github.sds100.keymapper.base.utils.ui.showDialog
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -21,17 +20,16 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
+import javax.inject.Inject
import kotlin.math.roundToInt
-/**
- * Created by sds100 on 03/08/20.
- */
-
-class PickDisplayCoordinateViewModel(
+@HiltViewModel
+class PickDisplayCoordinateViewModel @Inject constructor(
resourceProvider: ResourceProvider,
+ dialogProvider: DialogProvider,
) : ViewModel(),
ResourceProvider by resourceProvider,
- PopupViewModel by PopupViewModelImpl() {
+ DialogProvider by dialogProvider {
private val x = MutableStateFlow(null)
private val y = MutableStateFlow(null)
@@ -69,11 +67,11 @@ class PickDisplayCoordinateViewModel(
(displaySize.y != newBitmap.width && displaySize.x != newBitmap.height)
) {
viewModelScope.launch {
- val snackBar = PopupUi.SnackBar(
+ val snackBar = DialogModel.SnackBar(
message = getString(R.string.toast_incorrect_screenshot_resolution),
)
- showPopup("incorrect_resolution", snackBar)
+ showDialog("incorrect_resolution", snackBar)
}
return
@@ -109,9 +107,9 @@ class PickDisplayCoordinateViewModel(
val x = x.value ?: return@launch
val y = y.value ?: return@launch
- val description = showPopup(
+ val description = showDialog(
"coordinate_description",
- PopupUi.Text(
+ DialogModel.Text(
getString(R.string.hint_tap_coordinate_title),
allowEmpty = true,
text = description.value ?: "",
@@ -136,13 +134,4 @@ class PickDisplayCoordinateViewModel(
super.onCleared()
}
-
- @Suppress("UNCHECKED_CAST")
- class Factory(
- private val resourceProvider: ResourceProvider,
- ) : ViewModelProvider.NewInstanceFactory() {
-
- override fun create(modelClass: Class): T =
- PickDisplayCoordinateViewModel(resourceProvider) as T
- }
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/uielement/ChooseUiElementScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/uielement/ChooseUiElementScreen.kt
similarity index 96%
rename from app/src/main/java/io/github/sds100/keymapper/actions/uielement/ChooseUiElementScreen.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/uielement/ChooseUiElementScreen.kt
index 340f304358..7da4fd9033 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/uielement/ChooseUiElementScreen.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/uielement/ChooseUiElementScreen.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions.uielement
+package io.github.sds100.keymapper.base.actions.uielement
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -49,13 +49,14 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.window.core.layout.WindowHeightSizeClass
import androidx.window.core.layout.WindowWidthSizeClass
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.compose.KeyMapperTheme
-import io.github.sds100.keymapper.util.State
-import io.github.sds100.keymapper.util.ui.compose.CheckBoxText
-import io.github.sds100.keymapper.util.ui.compose.KeyMapperDropdownMenu
-import io.github.sds100.keymapper.util.ui.compose.SearchAppBarActions
-import io.github.sds100.keymapper.util.ui.compose.WindowSizeClassExt.compareTo
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.compose.KeyMapperTheme
+import io.github.sds100.keymapper.base.utils.ui.compose.CheckBoxText
+import io.github.sds100.keymapper.base.utils.ui.compose.KeyMapperDropdownMenu
+import io.github.sds100.keymapper.base.utils.ui.compose.SearchAppBarActions
+import io.github.sds100.keymapper.base.utils.ui.compose.WindowSizeClassExt.compareTo
+import io.github.sds100.keymapper.common.utils.NodeInteractionType
+import io.github.sds100.keymapper.common.utils.State
@Composable
fun ChooseElementScreen(
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/uielement/InteractUiElementScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/uielement/InteractUiElementScreen.kt
similarity index 95%
rename from app/src/main/java/io/github/sds100/keymapper/actions/uielement/InteractUiElementScreen.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/uielement/InteractUiElementScreen.kt
index 7ff879f499..30c0d792c5 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/uielement/InteractUiElementScreen.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/uielement/InteractUiElementScreen.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.actions.uielement
+package io.github.sds100.keymapper.base.actions.uielement
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedContentTransitionScope
@@ -69,19 +69,20 @@ import androidx.navigation.compose.rememberNavController
import androidx.window.core.layout.WindowHeightSizeClass
import androidx.window.core.layout.WindowWidthSizeClass
import com.google.accompanist.drawablepainter.rememberDrawablePainter
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.compose.KeyMapperTheme
-import io.github.sds100.keymapper.compose.LocalCustomColorsPalette
-import io.github.sds100.keymapper.system.apps.ChooseAppScreen
-import io.github.sds100.keymapper.util.State
-import io.github.sds100.keymapper.util.drawable
-import io.github.sds100.keymapper.util.ui.compose.ComposeIconInfo
-import io.github.sds100.keymapper.util.ui.compose.KeyMapperDropdownMenu
-import io.github.sds100.keymapper.util.ui.compose.OptionsHeaderRow
-import io.github.sds100.keymapper.util.ui.compose.WindowSizeClassExt.compareTo
-import io.github.sds100.keymapper.util.ui.compose.icons.AdGroup
-import io.github.sds100.keymapper.util.ui.compose.icons.JumpToElement
-import io.github.sds100.keymapper.util.ui.compose.icons.KeyMapperIcons
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.compose.KeyMapperTheme
+import io.github.sds100.keymapper.base.compose.LocalCustomColorsPalette
+import io.github.sds100.keymapper.base.system.apps.ChooseAppScreen
+import io.github.sds100.keymapper.base.utils.ui.compose.ComposeIconInfo
+import io.github.sds100.keymapper.base.utils.ui.compose.KeyMapperDropdownMenu
+import io.github.sds100.keymapper.base.utils.ui.compose.OptionsHeaderRow
+import io.github.sds100.keymapper.base.utils.ui.compose.WindowSizeClassExt.compareTo
+import io.github.sds100.keymapper.base.utils.ui.compose.icons.AdGroup
+import io.github.sds100.keymapper.base.utils.ui.compose.icons.JumpToElement
+import io.github.sds100.keymapper.base.utils.ui.compose.icons.KeyMapperIcons
+import io.github.sds100.keymapper.base.utils.ui.drawable
+import io.github.sds100.keymapper.common.utils.NodeInteractionType
+import io.github.sds100.keymapper.common.utils.State
import kotlinx.coroutines.flow.update
private const val DEST_LANDING = "landing"
@@ -92,7 +93,6 @@ private const val DEST_SELECT_ELEMENT = "select_element"
fun InteractUiElementScreen(
modifier: Modifier = Modifier,
viewModel: InteractUiElementViewModel,
- navigateBack: () -> Unit,
) {
val navController = rememberNavController()
@@ -107,7 +107,7 @@ fun InteractUiElementScreen(
val onBackClick = {
if (!navController.navigateUp()) {
- navigateBack()
+ viewModel.onBackClick()
}
}
@@ -381,11 +381,11 @@ private fun RecordingSection(
openSelectAppScreen: () -> Unit = {},
) {
Column(modifier = modifier) {
- when (state) {
+ when (val state = state) {
is State.Data -> {
- val interactionCount: Int = when (state.data) {
- is RecordUiElementState.CountingDown -> state.data.interactionCount
- is RecordUiElementState.Recorded -> state.data.interactionCount
+ val interactionCount: Int = when (val data = state.data) {
+ is RecordUiElementState.CountingDown -> data.interactionCount
+ is RecordUiElementState.Recorded -> data.interactionCount
RecordUiElementState.Empty -> 0
}
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/uielement/InteractUiElementUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/uielement/InteractUiElementUseCase.kt
similarity index 64%
rename from app/src/main/java/io/github/sds100/keymapper/actions/uielement/InteractUiElementUseCase.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/uielement/InteractUiElementUseCase.kt
index 9a325f22f3..e97be98810 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/uielement/InteractUiElementUseCase.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/uielement/InteractUiElementUseCase.kt
@@ -1,16 +1,16 @@
-package io.github.sds100.keymapper.actions.uielement
+package io.github.sds100.keymapper.base.actions.uielement
import android.graphics.drawable.Drawable
+import io.github.sds100.keymapper.base.system.accessibility.RecordAccessibilityNodeEvent
+import io.github.sds100.keymapper.base.system.accessibility.RecordAccessibilityNodeState
+import io.github.sds100.keymapper.common.utils.KMResult
+import io.github.sds100.keymapper.common.utils.State
+import io.github.sds100.keymapper.common.utils.mapData
+import io.github.sds100.keymapper.common.utils.onFailure
import io.github.sds100.keymapper.data.entities.AccessibilityNodeEntity
import io.github.sds100.keymapper.data.repositories.AccessibilityNodeRepository
-import io.github.sds100.keymapper.system.accessibility.RecordAccessibilityNodeState
-import io.github.sds100.keymapper.system.accessibility.ServiceAdapter
+import io.github.sds100.keymapper.system.accessibility.AccessibilityServiceAdapter
import io.github.sds100.keymapper.system.apps.PackageManagerAdapter
-import io.github.sds100.keymapper.util.Result
-import io.github.sds100.keymapper.util.ServiceEvent
-import io.github.sds100.keymapper.util.State
-import io.github.sds100.keymapper.util.mapData
-import io.github.sds100.keymapper.util.onFailure
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -20,10 +20,13 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
+import javax.inject.Inject
+import javax.inject.Singleton
-class InteractUiElementController(
+@Singleton
+class InteractUiElementController @Inject constructor(
private val coroutineScope: CoroutineScope,
- private val serviceAdapter: ServiceAdapter,
+ private val serviceAdapter: AccessibilityServiceAdapter,
private val nodeRepository: AccessibilityNodeRepository,
private val packageManagerAdapter: PackageManagerAdapter,
) : InteractUiElementUseCase {
@@ -41,7 +44,7 @@ class InteractUiElementController(
init {
serviceAdapter.eventReceiver
- .filterIsInstance()
+ .filterIsInstance()
.onEach { event -> recordState.update { event.state } }
.launchIn(coroutineScope)
}
@@ -58,17 +61,17 @@ class InteractUiElementController(
return nodeRepository.get(id)
}
- override fun getAppName(packageName: String): Result = packageManagerAdapter.getAppName(packageName)
+ override fun getAppName(packageName: String): KMResult = packageManagerAdapter.getAppName(packageName)
- override fun getAppIcon(packageName: String): Result = packageManagerAdapter.getAppIcon(packageName)
+ override fun getAppIcon(packageName: String): KMResult = packageManagerAdapter.getAppIcon(packageName)
- override suspend fun startRecording(): Result<*> {
+ override suspend fun startRecording(): KMResult<*> {
nodeRepository.deleteAll()
- return serviceAdapter.send(ServiceEvent.StartRecordingNodes)
+ return serviceAdapter.send(RecordAccessibilityNodeEvent.StartRecordingNodes)
}
override suspend fun stopRecording() {
- serviceAdapter.send(ServiceEvent.StopRecordingNodes).onFailure {
+ serviceAdapter.send(RecordAccessibilityNodeEvent.StopRecordingNodes).onFailure {
recordState.update { RecordAccessibilityNodeState.Idle }
}
}
@@ -86,10 +89,10 @@ interface InteractUiElementUseCase {
fun getInteractionsByPackage(packageName: String): Flow>>
suspend fun getInteractionById(id: Long): AccessibilityNodeEntity?
- fun getAppName(packageName: String): Result
- fun getAppIcon(packageName: String): Result
+ fun getAppName(packageName: String): KMResult
+ fun getAppIcon(packageName: String): KMResult
- suspend fun startRecording(): Result<*>
+ suspend fun startRecording(): KMResult<*>
suspend fun stopRecording()
fun startService(): Boolean
diff --git a/app/src/main/java/io/github/sds100/keymapper/actions/uielement/InteractUiElementViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/uielement/InteractUiElementViewModel.kt
similarity index 88%
rename from app/src/main/java/io/github/sds100/keymapper/actions/uielement/InteractUiElementViewModel.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/actions/uielement/InteractUiElementViewModel.kt
index 67dcac391c..b6d3f180a4 100644
--- a/app/src/main/java/io/github/sds100/keymapper/actions/uielement/InteractUiElementViewModel.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/uielement/InteractUiElementViewModel.kt
@@ -1,38 +1,36 @@
-package io.github.sds100.keymapper.actions.uielement
+package io.github.sds100.keymapper.base.actions.uielement
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Android
import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
-import io.github.sds100.keymapper.R
-import io.github.sds100.keymapper.actions.ActionData
+import dagger.hilt.android.lifecycle.HiltViewModel
+import io.github.sds100.keymapper.base.R
+import io.github.sds100.keymapper.base.actions.ActionData
+import io.github.sds100.keymapper.base.system.accessibility.RecordAccessibilityNodeState
+import io.github.sds100.keymapper.base.utils.containsQuery
+import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider
+import io.github.sds100.keymapper.base.utils.ui.DialogProvider
+import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
+import io.github.sds100.keymapper.base.utils.ui.ViewModelHelper
+import io.github.sds100.keymapper.base.utils.ui.compose.ComposeIconInfo
+import io.github.sds100.keymapper.base.utils.ui.compose.SimpleListItemModel
+import io.github.sds100.keymapper.common.utils.KMError
+import io.github.sds100.keymapper.common.utils.NodeInteractionType
+import io.github.sds100.keymapper.common.utils.State
+import io.github.sds100.keymapper.common.utils.Success
+import io.github.sds100.keymapper.common.utils.dataOrNull
+import io.github.sds100.keymapper.common.utils.ifIsData
+import io.github.sds100.keymapper.common.utils.mapData
+import io.github.sds100.keymapper.common.utils.onFailure
+import io.github.sds100.keymapper.common.utils.then
+import io.github.sds100.keymapper.common.utils.valueOrNull
import io.github.sds100.keymapper.data.entities.AccessibilityNodeEntity
-import io.github.sds100.keymapper.system.accessibility.RecordAccessibilityNodeState
-import io.github.sds100.keymapper.util.Error
-import io.github.sds100.keymapper.util.State
-import io.github.sds100.keymapper.util.Success
-import io.github.sds100.keymapper.util.containsQuery
-import io.github.sds100.keymapper.util.dataOrNull
-import io.github.sds100.keymapper.util.ifIsData
-import io.github.sds100.keymapper.util.mapData
-import io.github.sds100.keymapper.util.onFailure
-import io.github.sds100.keymapper.util.then
-import io.github.sds100.keymapper.util.ui.PopupViewModel
-import io.github.sds100.keymapper.util.ui.PopupViewModelImpl
-import io.github.sds100.keymapper.util.ui.ResourceProvider
-import io.github.sds100.keymapper.util.ui.ViewModelHelper
-import io.github.sds100.keymapper.util.ui.compose.ComposeIconInfo
-import io.github.sds100.keymapper.util.ui.compose.SimpleListItemModel
-import io.github.sds100.keymapper.util.valueOrNull
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
@@ -42,17 +40,20 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
+import kotlinx.serialization.json.Json
import java.util.Locale
+import javax.inject.Inject
-class InteractUiElementViewModel(
+@HiltViewModel
+class InteractUiElementViewModel @Inject constructor(
private val useCase: InteractUiElementUseCase,
- private val resourceProvider: ResourceProvider,
+ resourceProvider: ResourceProvider,
+ dialogProvider: DialogProvider,
+ navigationProvider: NavigationProvider,
) : ViewModel(),
- ResourceProvider by resourceProvider,
- PopupViewModel by PopupViewModelImpl() {
-
- private val _returnAction: MutableSharedFlow = MutableSharedFlow()
- val returnAction: SharedFlow = _returnAction.asSharedFlow()
+ NavigationProvider by navigationProvider,
+ DialogProvider by dialogProvider,
+ ResourceProvider by resourceProvider {
val recordState: StateFlow> = combine(
useCase.recordState,
@@ -250,7 +251,13 @@ class InteractUiElementViewModel(
)
viewModelScope.launch {
- _returnAction.emit(action)
+ popBackStackWithResult(Json.encodeToString(action))
+ }
+ }
+
+ fun onBackClick() {
+ viewModelScope.launch {
+ popBackStack()
}
}
@@ -338,13 +345,13 @@ class InteractUiElementViewModel(
private suspend fun startRecording() {
useCase.startRecording().onFailure { error ->
- if (error == Error.AccessibilityServiceDisabled) {
+ if (error == KMError.AccessibilityServiceDisabled) {
ViewModelHelper.handleAccessibilityServiceStoppedDialog(
this,
this,
startService = { useCase.startService() },
)
- } else if (error == Error.AccessibilityServiceCrashed) {
+ } else if (error == KMError.AccessibilityServiceCrashed) {
ViewModelHelper.handleAccessibilityServiceCrashedDialog(
this,
this,
@@ -408,16 +415,6 @@ class InteractUiElementViewModel(
NodeInteractionType.COLLAPSE -> getString(R.string.action_interact_ui_element_interaction_type_collapse)
}
}
-
- @Suppress("UNCHECKED_CAST")
- class Factory(
- private val useCase: InteractUiElementUseCase,
- private val resourceProvider: ResourceProvider,
- ) : ViewModelProvider.NewInstanceFactory() {
- override fun create(modelClass: Class): T {
- return InteractUiElementViewModel(useCase, resourceProvider) as T
- }
- }
}
data class SelectedUiElementState(
diff --git a/app/src/main/java/io/github/sds100/keymapper/backup/BackupContent.kt b/base/src/main/java/io/github/sds100/keymapper/base/backup/BackupContent.kt
similarity index 98%
rename from app/src/main/java/io/github/sds100/keymapper/backup/BackupContent.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/backup/BackupContent.kt
index 07a71956d1..da11d2b829 100644
--- a/app/src/main/java/io/github/sds100/keymapper/backup/BackupContent.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/backup/BackupContent.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.backup
+package io.github.sds100.keymapper.base.backup
import com.google.gson.annotations.SerializedName
import io.github.sds100.keymapper.data.entities.FloatingButtonEntity
diff --git a/app/src/main/java/io/github/sds100/keymapper/backup/BackupManager.kt b/base/src/main/java/io/github/sds100/keymapper/base/backup/BackupManager.kt
similarity index 91%
rename from app/src/main/java/io/github/sds100/keymapper/backup/BackupManager.kt
rename to base/src/main/java/io/github/sds100/keymapper/base/backup/BackupManager.kt
index bb82002476..84a81aa5ff 100644
--- a/app/src/main/java/io/github/sds100/keymapper/backup/BackupManager.kt
+++ b/base/src/main/java/io/github/sds100/keymapper/base/backup/BackupManager.kt
@@ -1,4 +1,4 @@
-package io.github.sds100.keymapper.backup
+package io.github.sds100.keymapper.base.backup
import com.github.salomonbrys.kotson.byInt
import com.github.salomonbrys.kotson.byNullableArray
@@ -14,8 +14,20 @@ import com.google.gson.JsonArray
import com.google.gson.JsonParser
import com.google.gson.JsonSyntaxException
import com.google.gson.stream.MalformedJsonException
-import io.github.sds100.keymapper.Constants
-import io.github.sds100.keymapper.actions.sound.SoundsManager
+import io.github.sds100.keymapper.base.actions.sound.SoundsManager
+import io.github.sds100.keymapper.common.BuildConfigProvider
+import io.github.sds100.keymapper.common.utils.DefaultDispatcherProvider
+import io.github.sds100.keymapper.common.utils.DefaultUuidGenerator
+import io.github.sds100.keymapper.common.utils.DispatcherProvider
+import io.github.sds100.keymapper.common.utils.KMError
+import io.github.sds100.keymapper.common.utils.KMResult
+import io.github.sds100.keymapper.common.utils.State
+import io.github.sds100.keymapper.common.utils.Success
+import io.github.sds100.keymapper.common.utils.TreeNode
+import io.github.sds100.keymapper.common.utils.UuidGenerator
+import io.github.sds100.keymapper.common.utils.breadFirstTraversal
+import io.github.sds100.keymapper.common.utils.onFailure
+import io.github.sds100.keymapper.common.utils.then
import io.github.sds100.keymapper.data.Keys
import io.github.sds100.keymapper.data.PreferenceDefaults
import io.github.sds100.keymapper.data.db.AppDatabase
@@ -42,23 +54,11 @@ import io.github.sds100.keymapper.data.migration.fingerprintmaps.FingerprintToKe
import io.github.sds100.keymapper.data.repositories.FloatingButtonRepository
import io.github.sds100.keymapper.data.repositories.FloatingLayoutRepository
import io.github.sds100.keymapper.data.repositories.GroupRepository
+import io.github.sds100.keymapper.data.repositories.KeyMapRepository
import io.github.sds100.keymapper.data.repositories.PreferenceRepository
import io.github.sds100.keymapper.data.repositories.RepositoryUtils
-import io.github.sds100.keymapper.keymaps.KeyMapRepository
import io.github.sds100.keymapper.system.files.FileAdapter
import io.github.sds100.keymapper.system.files.IFile
-import io.github.sds100.keymapper.util.DefaultDispatcherProvider
-import io.github.sds100.keymapper.util.DefaultUuidGenerator
-import io.github.sds100.keymapper.util.DispatcherProvider
-import io.github.sds100.keymapper.util.Error
-import io.github.sds100.keymapper.util.Result
-import io.github.sds100.keymapper.util.State
-import io.github.sds100.keymapper.util.Success
-import io.github.sds100.keymapper.util.TreeNode
-import io.github.sds100.keymapper.util.UuidGenerator
-import io.github.sds100.keymapper.util.breadFirstTraversal
-import io.github.sds100.keymapper.util.onFailure
-import io.github.sds100.keymapper.util.then
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -74,12 +74,11 @@ import java.io.IOException
import java.io.InputStream
import java.util.LinkedList
import java.util.UUID
+import javax.inject.Inject
+import javax.inject.Singleton
-/**
- * Created by sds100 on 16/03/2021.
- */
-
-class BackupManagerImpl(
+@Singleton
+class BackupManagerImpl @Inject constructor(
private val coroutineScope: CoroutineScope,
private val fileAdapter: FileAdapter,
private val keyMapRepository: KeyMapRepository,
@@ -88,9 +87,9 @@ class BackupManagerImpl(
private val floatingButtonRepository: FloatingButtonRepository,
private val groupRepository: GroupRepository,
private val soundsManager: SoundsManager,
- private val throwExceptions: Boolean = false,
private val dispatchers: DispatcherProvider = DefaultDispatcherProvider(),
private val uuidGenerator: UuidGenerator = DefaultUuidGenerator(),
+ private val buildConfigProvider: BuildConfigProvider,
) : BackupManager {
companion object {
@@ -108,7 +107,7 @@ class BackupManagerImpl(
private const val TEMP_RESTORE_ROOT_DIR = "restore_temp"
}
- override val onAutomaticBackupResult = MutableSharedFlow>()
+ override val onAutomaticBackupResult = MutableSharedFlow>()
private val gson: Gson by lazy {
GsonBuilder()
@@ -151,7 +150,7 @@ class BackupManagerImpl(
}
}
- override suspend fun backupKeyMaps(output: IFile, keyMapIds: List): Result {
+ override suspend fun backupKeyMaps(output: IFile, keyMapIds: List): KMResult {
return withContext(dispatchers.default()) {
val allKeyMaps = keyMapRepository.keyMapList
.filterIsInstance>>()
@@ -165,7 +164,7 @@ class BackupManagerImpl(
}
}
- override suspend fun backupEverything(output: IFile): Result {
+ override suspend fun backupEverything(output: IFile): KMResult {
return withContext(dispatchers.io()) {
val keyMaps =
keyMapRepository.keyMapList
@@ -184,7 +183,7 @@ class BackupManagerImpl(
}
}
- override suspend fun getBackupContent(file: IFile): Result {
+ override suspend fun getBackupContent(file: IFile): KMResult {
return extractFile(file).then { extractedDir ->
val dataJsonFile = fileAdapter.getFile(extractedDir, DATA_JSON_FILE_NAME)
@@ -192,20 +191,20 @@ class BackupManagerImpl(
val inputStream = dataJsonFile.inputStream()
if (inputStream == null) {
- return Error.UnknownIOError
+ return KMError.UnknownIOError
}
return parseBackupContent(inputStream)
}
}
- private suspend fun parseBackupContent(jsonFile: InputStream): Result = withContext(dispatchers.io()) {
+ private suspend fun parseBackupContent(jsonFile: InputStream): KMResult = withContext(dispatchers.io()) {
try {
val rootElement = jsonFile.bufferedReader().use {
val element = JsonParser().parse(it)
if (element.isJsonNull) {
- return@withContext Error.EmptyJson
+ return@withContext KMError.EmptyJson
}
element.asJsonObject
@@ -214,12 +213,12 @@ class BackupManagerImpl(
val backupDbVersion = rootElement.get(BackupContent.NAME_DB_VERSION).nullInt ?: 9
val backupAppVersion = rootElement.get(BackupContent.NAME_APP_VERSION).nullInt
- if (backupAppVersion != null && backupAppVersion > Constants.VERSION_CODE) {
- return@withContext Error.BackupVersionTooNew
+ if (backupAppVersion != null && backupAppVersion > buildConfigProvider.versionCode) {
+ return@withContext KMError.BackupVersionTooNew
}
if (backupDbVersion > AppDatabase.DATABASE_VERSION) {
- return@withContext Error.BackupVersionTooNew
+ return@withContext KMError.BackupVersionTooNew
}
val keyMapListJsonArray by rootElement.byNullableArray(BackupContent.NAME_KEYMAP_LIST)
@@ -344,7 +343,7 @@ class BackupManagerImpl(
}
if (backupVersionTooNew) {
- return@withContext Error.BackupVersionTooNew
+ return@withContext KMError.BackupVersionTooNew
}
}
@@ -388,23 +387,19 @@ class BackupManagerImpl(
return@withContext Success(content)
} catch (e: MalformedJsonException) {
- return@withContext Error.CorruptJsonFile(e.message ?: "")
+ return@withContext KMError.CorruptJsonFile(e.message ?: "")
} catch (e: JsonSyntaxException) {
- return@withContext Error.CorruptJsonFile(e.message ?: "")
+ return@withContext KMError.CorruptJsonFile(e.message ?: "")
} catch (e: NoSuchElementException) {
- return@withContext Error.CorruptJsonFile(e.message ?: "")
+ return@withContext KMError.CorruptJsonFile(e.message ?: "")
} catch (e: Exception) {
Timber.e(e)
- if (throwExceptions) {
- throw e
- }
-
- return@withContext Error.Exception(e)
+ return@withContext KMError.Exception(e)
}
}
- override suspend fun restore(file: IFile, restoreType: RestoreType): Result<*> {
+ override suspend fun restore(file: IFile, restoreType: RestoreType): KMResult<*> {
return extractFile(file).then { extractedDir ->
val dataJsonFile = fileAdapter.getFile(extractedDir, DATA_JSON_FILE_NAME)
@@ -412,7 +407,7 @@ class BackupManagerImpl(
val inputStream = dataJsonFile.inputStream()
if (inputStream == null) {
- return@then Error.UnknownIOError
+ return@then KMError.UnknownIOError
}
val soundDir = fileAdapter.getFile(extractedDir, SOUNDS_DIR_NAME)
@@ -432,7 +427,7 @@ class BackupManagerImpl(
/**
* @return the directory with the extracted contents.
*/
- private suspend fun extractFile(file: IFile): Result {
+ private suspend fun extractFile(file: IFile): KMResult {
val restoreUuid = uuidGenerator.random()
val zipDestination =
@@ -451,7 +446,7 @@ class BackupManagerImpl(
return Success(zipDestination)
} catch (e: IOException) {
- return Error.UnknownIOError
+ return KMError.UnknownIOError
}
}
@@ -460,7 +455,7 @@ class BackupManagerImpl(
backupContent: BackupContent,
soundFiles: List,
currentTime: Long,
- ): Result<*> {
+ ): KMResult<*> {
try {
// MUST come before restoring key maps so it is possible to
// validate that each key map's group exists in the repository.
@@ -557,19 +552,15 @@ class BackupManagerImpl(
return Success(Unit)
} catch (e: MalformedJsonException) {
- return Error.CorruptJsonFile(e.message ?: "")
+ return KMError.CorruptJsonFile(e.message ?: "")
} catch (e: JsonSyntaxException) {
- return Error.CorruptJsonFile(e.message ?: "")
+ return KMError.CorruptJsonFile(e.message ?: "")
} catch (e: NoSuchElementException) {
- return Error.CorruptJsonFile(e.message ?: "")
+ return KMError.CorruptJsonFile(e.message ?: "")
} catch (e: Exception) {
Timber.e(e)
- if (throwExceptions) {
- throw e
- }
-
- return Error.Exception(e)
+ return KMError.Exception(e)
}
}
@@ -692,7 +683,7 @@ class BackupManagerImpl(
keyMapList: List = emptyList(),
extraGroups: List = emptyList(),
extraLayouts: List